这篇文章咱们来说讲模型绑定(Model Binding),其实在初步了解ASP.NET MVC以后,你们可能都会产生一个疑问,为何URL片断最后会转换为例如int型或者其余类型的参数呢?这里就不得不说模型绑定了。模型绑定是指,用浏览器以HTTP请求方式发送的数据来建立.NET对象的过程。每当定义具备参数的动做方法时,一直是在依赖着这种模型绑定过程。html
准备项目浏览器
咱们先来建立一个MVC项目,名叫MVCModels,并在Models文件夹中建立一个新的类文件Person。并发
1 using System; 2 3 namespace MVCModels.Models 4 { 5 public class Person 6 { 7 public int PersonId { get; set; } 8 public string FirstName { get; set; } 9 public string LastName { get; set; } 10 public DateTime BirthDate { get; set; } 11 public Address HomeAddress { get; set; } 12 public Role Role { get; set; } 13 } 14 15 public class Address 16 { 17 public string Line { get; set; } 18 public string City { get; set; } 19 public string PostalCode { get; set; } 20 public string Country { get; set; } 21 } 22 23 public enum Role 24 { 25 Admin, 26 User, 27 Guest 28 } 29 }
另外定义一个Home控制器。app
1 using MVCModels.Models; 2 using System.Linq; 3 using System.Web.Mvc; 4 5 namespace MVCModels.Controllers 6 { 7 public class HomeController : Controller 8 { 9 private Person[] personDate = { 10 new Person { PersonId = 1, FirstName = "Adam", LastName = "Freeman", Role = Role.Admin }, 11 new Person { PersonId = 2, FirstName = "Jacqui", LastName = "Griffyth", Role = Role.User }, 12 new Person { PersonId = 1, FirstName = "John", LastName = "Smith", Role = Role.Guest }, 13 }; 14 public ActionResult Index(int id) 15 { 16 Person dataItem = personDate.Where(p => p.PersonId == id).First(); 17 return View(dataItem); 18 } 19 } 20 }
接下来再建立一个视图文件Index。ui
@model MVCModels.Models.Person @{ ViewBag.Title = "Index"; Layout = "~/Views/Shared/_Layout.cshtml"; } <h2>Person</h2> <div> <label>ID:</label> @Html.DisplayFor(m => m.PersonId) </div> <div> <label>First Name:</label> @Html.DisplayFor(m => m.FirstName) </div> <div> <label>Last Name:</label> @Html.DisplayFor(m => m.LastName) </div> <div> <label>Role:</label> @Html.DisplayFor(m => m.Role) </div>
理解模型绑定spa
模型绑定是HTTP请求与C#方法之间的一个桥梁,它根据 Action 方法中的 Model 类型建立 .NET 对象,并将 HTTP 请求数据通过转换赋给该对象。当咱们启动项目,并导航到/Home/Index/1,咱们会看见图下:code
当咱们请求 /Home/Index/1 URL 时,路由系统便将最后一个片断值 1 赋给了 id 变量。action invoker 经过路由信息知道当前的请求须要 Index action 方法来处理,但它调用 Index action 方法以前必须先拿到该方法参数的值。 默认的动做调用器ControllerActionInvoker,要依靠模型绑定器来生成调用动做所须要的的数据对象,模型绑定器由IModelBinder接口所定义。在本例中,动做调用器会检查Index方法,并发现它具备一个int型参数,因而会查找负责int值绑定的绑定器,并调用它的BindModel方法。orm
使用默认模型绑定器htm
虽然应用程序能够自定义模型绑定器,可是大多数状况下仍是依靠内建的绑定器类DefaultModelBinder。当动做调用器找不到绑定某个类型的自定义绑定器时,就会使用这个默认的模型绑定器,默认状况下,这个模型绑定器会搜索四个位置。以下表所示:对象
源 | 描述 |
Request.From | 由用户在HTML的表单元素中提供值 |
RouteData.Values | 用应用程序路由得到的值 |
Request.QueryString | 包含在请求URL中的查询字符串部分的数据 |
Request.Files | 请求中上传的文件 |
这些位置将被依次搜索,在本例中,DefaultModelBinder会为id参数查找如下的一个值:
1.Request.Form["id"]
2.RouteData.Values["id"]
3.Request.QueryString["id"]
4.Request.Files["id"]
绑定简单类型
处理简单参数类型时,DefaultModelBinder会使用System.ComponentModel.TypeDescriptor类,讲请求数据得到的字符参数值转换为参数类型,若是没法转换,那么DefaultModelBinder便不能绑定到Model。好比访问/Home/Index/apple,便会出现如图所示:
默认模型绑定器看到须要的是int值,若是视图将URL中提供的apple值转换为int型,就会出错,此时咱们能够修改代码,为参数提供一个默认值,这样当默认绑定器没法转换时也不会出错了。
1 ... 2 public ActionResult Index(int id = 1) 3 { 4 Person dataItem = personDate.Where(p => p.PersonId == id).First(); 5 return View(dataItem); 6 }
绑定复杂类型
当动做方法参数是符合类型时(即不能用TypeConverter类进行转换的属性),DefaultModelBinder类将用反射类获取public属性集。好的,咱们如今来修改代码。
1 using MVCModels.Models; 2 using System.Linq; 3 using System.Web.Mvc; 4 5 namespace MVCModels.Controllers 6 { 7 public class HomeController : Controller 8 { 9 private Person[] personDate = { 10 new Person { PersonId = 1, FirstName = "Adam", LastName = "Freeman", Role = Role.Admin }, 11 new Person { PersonId = 2, FirstName = "Jacqui", LastName = "Griffyth", Role = Role.User }, 12 new Person { PersonId = 1, FirstName = "John", LastName = "Smith", Role = Role.Guest }, 13 }; 14 public ActionResult Index(int id = 1) 15 { 16 Person dataItem = personDate.Where(p => p.PersonId == id).First(); 17 return View(dataItem); 18 } 19 public ActionResult CreatePerson() 20 { 21 return View(new Person()); 22 } 23 [HttpPost] 24 public ActionResult CreatePerson(Person model) 25 { 26 return View("Index", model); 27 } 28 } 29 }
另外建立一个CreatePerson视图。
1 @model MVCModels.Models.Person 2 @{ 3 ViewBag.Title = "CreatePerson"; 4 Layout = "~/Views/Shared/_Layout.cshtml"; 5 } 6 7 <h2>CreatePerson</h2> 8 @using (Html.BeginForm()) 9 { 10 <div> 11 <label> 12 @Html.LabelFor(m => m.PersonId) 13 @Html.EditorFor(m => m.PersonId) 14 </label> 15 </div> 16 <div> 17 <label> 18 @Html.LabelFor(m => m.FirstName) 19 @Html.EditorFor(m => m.FirstName) 20 </label> 21 </div> 22 <div> 23 <label> 24 @Html.LabelFor(m => m.LastName) 25 @Html.EditorFor(m => m.LastName) 26 </label> 27 </div> 28 <div> 29 <label> 30 @Html.LabelFor(m => m.Role) 31 @Html.EditorFor(m => m.Role) 32 </label> 33 </div> 34 <button type="submit">Submit</button> 35 }
在表单回递给CreatePerson方法时,默认绑定器发现动做方法须要一个Person对象,便会依次处理每一个属性。绑定器会从请求中找到每个值。若是一个属性须要另外一个复合类型时,那么该过程会重复执行。例如本例中,Person类的HomeAddress属性是Address类型,在为Line属性查找值时,模型绑定器查找的是HomeAddress.Line的值,即模型对象的属性名(HomeAddress)与属性类型(Address)的属性名(Line)的组合。
Bind特性
咱们还能够用bind特性为 Address 类型的参数绑定 Person 对象中的 HomeAddress 属性值,例如这样:
1 public ActionResult DisplayAddress([Bind(Prefix="HomeAddress")]Address address) 2 { 3 return View(address); 4 }
DisplayAddress action 方法的参数类型 Address 不必定必须是 Person 的 HomeAddress 属性的类型,它能够是其余类型,只要该类型中含有City
或 Country 同名的属性就都会被绑定到。不过,要注意的是,使用 Bind 特性指定了前缀后,须要提交的表单元素的 name 属性必须有该前缀才能被绑定。Bind 特性还有两个属性,Exclude 和 Include。它们能够指定在 Mdoel 的属性中,Binder 不查找或只查找某个属性,即在查找时要么只包含这个属性要么不包含这个属性。以下面的 action 方法:
1 public ActionResult DisplayAddress([Bind(Prefix = "HomeAddress", Exclude = "Country")]Address address) 2 { 3 return View(address); 4 }
这时 Binder 在绑定时不会对 Address 这个 Model 的 Country 属性绑定值。