1. Model任务
Model负责通过数据库、AD(Active Directory)、Web Service及其他方式获取数据,以及将用户输入的数据保存到数据库、AD、Web Service等中。
Model只专注于有效地提供数据访问机制、数据格式验证、业务逻辑验证等。
2. 定义Model Metadata
Metadata用于定义数据模型的相关属性,如:显示名称、数据长度及数据格式验证等。利用System.ComponentModel.DataAnnotations中的DataAnnotations机制对ASP.NET MVC数据模型进行辅助定义。
System.ComponentModel.DataAnnotations命名空间的验证属性包括:StringLength、Required、RegularExpression及Range等。
示例:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web; using System.ComponentModel;
using System.ComponentModel.DataAnnotations; namespace Libing.Portal.Web.Models
{
public class Product
{
public int ProductID { get; set; } [DisplayName("产品名称")]
[Required(ErrorMessage = "产品名称不能为空")]
[StringLength(, ErrorMessage = "产品名称最大长度100个字符")]
public string ProductName { get; set; } [Required]
[RegularExpression(@"^\d+$", ErrorMessage = "库存数量只能为数字")]
[Range(, , ErrorMessage = "库存数量0至100之间")]
public int UnitsInStock { get; set; }
}
}
3. 自定义Metadata验证属性
定义Metadata验证属性:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web; using System.ComponentModel.DataAnnotations; namespace Libing.Portal.Web.Models.Attributes
{
/// <summary>
/// 验证正整数
/// </summary>
public class PositiveIntegerAttribute : RegularExpressionAttribute
{
public PositiveIntegerAttribute() :
base(@"^\d+$") { }
}
}
使用自定义Metadata验证属性:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web; using System.ComponentModel;
using System.ComponentModel.DataAnnotations; using Libing.Portal.Web.Models.Attributes; namespace Libing.Portal.Web.Models
{
public class Product
{
[Required]
[PositiveInteger(ErrorMessage = "库存数量只能为正整数")]
[Range(, , ErrorMessage = "库存数量0至100之间")]
public int UnitsInStock { get; set; }
}
}
4. 模型绑定
ASP.NET MVC通过模型绑定(Model Binding)机制来解析客户端传送过来的数据,解析的工作由DefaultModelBinder类进行处理。若要自定义ModelBinder类行为,需实现IModelBinder接口。
4.1 简单模型绑定
Action的参数在Action被执行时会通过DefaultModelBinder从form或QueryString传送过来的数据进行处理,即将传送过来的字符串型的数据转换成对应的.Net类,并将其输入Action。
public ActionResult Index(string UserName)
{
ViewBag.UserName = UserName;
return View();
}
4.2 复杂模型绑定
在ASP.NET MVC中,可以通过DefaultModelBinder类将form数据对应到复杂的.NET类,即模型。该模型可能是一个List<T>类或一个含有多个属性的自定义类。
public ActionResult Index(Product product)
{
ViewBag.ProductName = product.ProductName;
return View();
}
从客户端传送过来的form数据会通过DefaultModelBinder类自动创建Product类对象,将form字段通过.NET的Reflection机制一一对应到对象的同名属性中。
4.3 模型绑定数据验证
ASP.NET MVC在处理模型绑定时,会处理Model的数据验证。模型绑定的数据验证失败,则Controller的ModelState.IsValid验证值为false。
[HttpPost]
public ActionResult Create(Product product)
{
if (ModelState.IsValid)
{
return RedirectToAction("Details", new { id = product.ProductID });
} return View();
}
可以使用ModelState.AddModelError()方法在Controller中判断更加复杂的业务逻辑,并自定义错误信息至ModelState。
[HttpPost]
public ActionResult Create(Product product)
{
if (ModelState.IsValid)
{
if (product.UnitsInStock <=)
{
ModelState.AddModelError("UnitsInStock", "UnitsInStock必须大于10");
return View();
} return RedirectToAction("Details", new { id = product.ProductID });
} return View();
}
4.4 使用Bind属性限制可被更新的Model属性
复杂模型绑定的验证,在默认情况下,不管Model中有多少字段,只要客户端form有数据传送过来就会自动进行绑定。在ASP.NET MVC中可以通过使用Bind属性限制可被更新的Model属性。
4.4.1 绑定多个字段中的部分字段
通过Bind属性来定义Model中需要绑定哪些字段。
示例:不包括的自动绑定的属性
[HttpPost]
public ActionResult Create([Bind(Exclude = "ProductID")] Product product)
{
if (ModelState.IsValid)
{
if (product.UnitsInStock <= )
{
ModelState.AddModelError("UnitsInStock", "UnitsInStock必须大于10");
return View();
} return RedirectToAction("Details", new { id = product.ProductID });
} return View();
}
示例:多个不包括的自动绑定的属性
多个字段需要排除,使用逗号(,)分隔。
[HttpPost]
public ActionResult Create([Bind(Exclude = "ProductID,CreateDate")] Product product)
{
if (ModelState.IsValid)
{
if (product.UnitsInStock <= )
{
ModelState.AddModelError("UnitsInStock", "UnitsInStock必须大于10");
return View();
} return RedirectToAction("Details", new { id = product.ProductID });
} return View();
}
示例:使用Include指定需要绑定的字段
[HttpPost]
public ActionResult Create([Bind(Include = "ProductName,UnitsInStock")] Product product)
{
if (ModelState.IsValid)
{
if (product.UnitsInStock <= )
{
ModelState.AddModelError("UnitsInStock", "UnitsInStock必须大于10");
return View();
} return RedirectToAction("Details", new { id = product.ProductID });
} return View();
}
如果不希望在每个Action的参数中都应用Bind属性,可以在Model定义中指定。
示例:在Model中设置Bind属性
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web; using System.Web.Mvc; namespace Libing.Portal.Web.Models
{
[Bind(Include = "ProductName,UnitsInStock")]
public class Product
{
public int ProductID { get; set; } public string ProductName { get; set; } public int UnitsInStock { get; set; } public DateTime CreateDate { get; set; }
}
}
4.4.2 UpdateModel()方法与TryUpdateModel()方法
当绑定引发异常时,使用UpdateModel()方法会直接抛出异常。使用TryUpdateModel()方法,则会在验证成功时返回true,失败或发生异常时返回false。
示例:UpdateModel()
[HttpPost]
public ActionResult Create(Product product)
{
UpdateModel(product); return RedirectToAction("Details", new { id = product.ProductID });
}
示例:TryUpdateModel()
[HttpPost]
public ActionResult Create(Product product)
{
if (TryUpdateModel(product))
{
return RedirectToAction("Details", new { id = product.ProductID });
} return View();
}
5. ViewBag与ViewModel
5.1 使用ViewBag
Controller基类提供了ViewBag,可用于将数据项从Controller传递到View中。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc; using Libing.Portal.Web.Models; namespace Libing.Portal.Web.Controllers
{
public class ProductController : Controller
{
private PortalContext context = new PortalContext(); public ActionResult Edit(int id)
{
Product product = context.Products.Find(id);
ViewBag.Categories = new SelectList(context.Categories, "CategoryID", "CategoryName", product.CategoryID); return View(product);
} protected override void Dispose(bool disposing)
{
context.Dispose();
base.Dispose(disposing);
}
}
}
@Html.DropDownList("CategoryID", ViewBag.Categories as SelectList)
5.2 使用ViewModel模式
ViewModel模式创建强类型的类,对特定View情形优化,并向View模板提供所需要的动态值或内容。Controller将这些ViewModel传递到View模板中显示。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web; using System.Web.Mvc; namespace Libing.Portal.Web.Models.ViewModels
{
public class ProductViewModel
{
public Product Product { get; set; } public SelectList Categories { get; set; }
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc; using Libing.Portal.Web.Models;
using Libing.Portal.Web.Models.ViewModels; namespace Libing.Portal.Web.Controllers
{
public class ProductController : Controller
{
private PortalContext context = new PortalContext(); public ActionResult Edit(int id)
{
Product product = context.Products.Find(id); ProductViewModel productViewModel = new ProductViewModel();
productViewModel.Product = product;
productViewModel.Categories = new SelectList(context.Categories, "CategoryID", "CategoryName", product.CategoryID); return View(productViewModel);
} protected override void Dispose(bool disposing)
{
context.Dispose();
base.Dispose(disposing);
}
}
}
@model Libing.Portal.Web.Models.ViewModels.ProductViewModel
@Html.DropDownList("CategoryID", Model.Categories)