查看: 1752|回复: 0

[ASP.NET教程] ASP.NET MVC——模型绑定

发表于 2016-12-31 08:00:08
尚学堂AD

这篇文章我们来讲讲模型绑定(Model Binding),其实在初步了解asp.net MVC之后,大家可能都会产生一个疑问,为什么URL片段最后会转换为例如int型或者其他类型的参数呢?这里就不得不说模型绑定了。模型绑定是指,用浏览器以HTTP请求方式发送的数据来创建.NET对象的过程。每当定义具有参数的动作方法时,一直是在依赖着这种模型绑定过程。

准备项目

我们先来创建一个MVC项目,名叫MVCModels,并在Models文件夹中创建一个新的类文件Person。

  1. 1 using System;
  2. 2
  3. 3 namespace MVCModels.Models
  4. 4 {
  5. 5 public class Person
  6. 6 {
  7. 7 public int PersonId { get; set; }
  8. 8 public string FirstName { get; set; }
  9. 9 public string LastName { get; set; }
  10. 10 public DateTime BirthDate { get; set; }
  11. 11 public Address HomeAddress { get; set; }
  12. 12 public Role Role { get; set; }
  13. 13 }
  14. 14
  15. 15 public class Address
  16. 16 {
  17. 17 public string Line { get; set; }
  18. 18 public string City { get; set; }
  19. 19 public string PostalCode { get; set; }
  20. 20 public string Country { get; set; }
  21. 21 }
  22. 22
  23. 23 public enum Role
  24. 24 {
  25. 25 Admin,
  26. 26 User,
  27. 27 Guest
  28. 28 }
  29. 29 }
复制代码

另外定义一个Home控制器。

  1. 1 using MVCModels.Models;
  2. 2 using System.Linq;
  3. 3 using System.Web.Mvc;
  4. 4
  5. 5 namespace MVCModels.Controllers
  6. 6 {
  7. 7 public class HomeController : Controller
  8. 8 {
  9. 9 private Person[] personDate = {
  10. 10 new Person { PersonId = 1, FirstName = "Adam", LastName = "Freeman", Role = Role.Admin },
  11. 11 new Person { PersonId = 2, FirstName = "Jacqui", LastName = "Griffyth", Role = Role.User },
  12. 12 new Person { PersonId = 1, FirstName = "John", LastName = "Smith", Role = Role.Guest },
  13. 13 };
  14. 14 public ActionResult Index(int id)
  15. 15 {
  16. 16 Person dataItem = personDate.Where(p => p.PersonId == id).First();
  17. 17 return View(dataItem);
  18. 18 }
  19. 19 }
  20. 20 }
复制代码

接下来再创建一个视图文件Index。

  1. @model MVCModels.Models.Person
  2. @{
  3. ViewBag.Title = "Index";
  4. Layout = "~/Views/Shared/_Layout.cshtml";
  5. }
  6. <h2>Person</h2>
  7. <div>
  8. <label>ID:</label>
  9. @Html.DisplayFor(m => m.PersonId)
  10. </div>
  11. <div>
  12. <label>First Name:</label>
  13. @Html.DisplayFor(m => m.FirstName)
  14. </div>
  15. <div>
  16. <label>Last Name:</label>
  17. @Html.DisplayFor(m => m.LastName)
  18. </div>
  19. <div>
  20. <label>Role:</label>
  21. @Html.DisplayFor(m => m.Role)
  22. </div>
复制代码

理解模型绑定

模型绑定是HTTP请求与C#方法之间的一个桥梁,它根据 Action 方法中的 Model 类型创建 .NET 对象,并将 HTTP 请求数据经过转换赋给该对象。当我们启动项目,并导航到/Home/Index/1,我们会看见图下:

当我们请求 /Home/Index/1 URL 时,路由系统便将最后一个片段值 1 赋给了 id 变量。action invoker 通过路由信息知道当前的请求需要 Index action 方法来处理,但它调用 Index action 方法之前必须先拿到该方法参数的值。 默认的动作调用器ControllerActionInvoker,要依靠模型绑定器来生成调用动作所需要的的数据对象,模型绑定器由IModelBinder接口所定义。在本例中,动作调用器会检查Index方法,并发现它具有一个int型参数,于是会查找负责int值绑定的绑定器,并调用它的BindModel方法。

使用默认模型绑定器

虽然应用程序可以自定义模型绑定器,但是大多数情况下还是依靠内建的绑定器类DefaultModelBinder。当动作调用器找不到绑定某个类型的自定义绑定器时,就会使用这个默认的模型绑定器,默认情况下,这个模型绑定器会搜索四个位置。如下表所示:

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. 1 ...
  2. 2 public ActionResult Index(int id = 1)
  3. 3 {
  4. 4 Person dataItem = personDate.Where(p => p.PersonId == id).First();
  5. 5 return View(dataItem);
  6. 6 }
复制代码

绑定复杂类型

当动作方法参数是符合类型时(即不能用TypeConverter类进行转换的属性),DefaultModelBinder类将用反射类获取public属性集。好的,我们现在来修改代码。

  1. 1 using MVCModels.Models;
  2. 2 using System.Linq;
  3. 3 using System.Web.Mvc;
  4. 4
  5. 5 namespace MVCModels.Controllers
  6. 6 {
  7. 7 public class HomeController : Controller
  8. 8 {
  9. 9 private Person[] personDate = {
  10. 10 new Person { PersonId = 1, FirstName = "Adam", LastName = "Freeman", Role = Role.Admin },
  11. 11 new Person { PersonId = 2, FirstName = "Jacqui", LastName = "Griffyth", Role = Role.User },
  12. 12 new Person { PersonId = 1, FirstName = "John", LastName = "Smith", Role = Role.Guest },
  13. 13 };
  14. 14 public ActionResult Index(int id = 1)
  15. 15 {
  16. 16 Person dataItem = personDate.Where(p => p.PersonId == id).First();
  17. 17 return View(dataItem);
  18. 18 }
  19. 19 public ActionResult CreatePerson()
  20. 20 {
  21. 21 return View(new Person());
  22. 22 }
  23. 23 [HttpPost]
  24. 24 public ActionResult CreatePerson(Person model)
  25. 25 {
  26. 26 return View("Index", model);
  27. 27 }
  28. 28 }
  29. 29 }
复制代码

另外创建一个CreatePerson视图。

  1. 1 @model MVCModels.Models.Person
  2. 2 @{
  3. 3 ViewBag.Title = "CreatePerson";
  4. 4 Layout = "~/Views/Shared/_Layout.cshtml";
  5. 5 }
  6. 6
  7. 7 <h2>CreatePerson</h2>
  8. 8 @using (Html.BeginForm())
  9. 9 {
  10. 10 <div>
  11. 11 <label>
  12. 12 @Html.LabelFor(m => m.PersonId)
  13. 13 @Html.EditorFor(m => m.PersonId)
  14. 14 </label>
  15. 15 </div>
  16. 16 <div>
  17. 17 <label>
  18. 18 @Html.LabelFor(m => m.FirstName)
  19. 19 @Html.EditorFor(m => m.FirstName)
  20. 20 </label>
  21. 21 </div>
  22. 22 <div>
  23. 23 <label>
  24. 24 @Html.LabelFor(m => m.LastName)
  25. 25 @Html.EditorFor(m => m.LastName)
  26. 26 </label>
  27. 27 </div>
  28. 28 <div>
  29. 29 <label>
  30. 30 @Html.LabelFor(m => m.Role)
  31. 31 @Html.EditorFor(m => m.Role)
  32. 32 </label>
  33. 33 </div>
  34. 34 <button type="submit">Submit</button>
  35. 35 }
复制代码

在表单回递给CreatePerson方法时,默认绑定器发现动作方法需要一个Person对象,便会依次处理每个属性。绑定器会从请求中找到每一个值。如果一个属性需要另一个复合类型时,那么该过程会重复执行。例如本例中,Person类的HomeAddress属性是Address类型,在为Line属性查找值时,模型绑定器查找的是HomeAddress.Line的值,即模型对象的属性名(HomeAddress)与属性类型(Address)的属性名(Line)的组合。

Bind特性

我们还可以用bind特性为 Address 类型的参数绑定 Person 对象中的 HomeAddress 属性值,例如这样:

  1. 1 public ActionResult DisplayAddress([Bind(Prefix="HomeAddress")]Address address)
  2. 2 {
  3. 3 return View(address);
  4. 4 }
复制代码

DisplayAddress action 方法的参数类型 Address 不一定必须是 Person 的 HomeAddress 属性的类型,它可以是其他类型,只要该类型中含有City

或 Country 同名的属性就都会被绑定到。不过,要注意的是,使用 Bind 特性指定了前缀后,需要提交的表单元素的 name 属性必须有该前缀才能被绑定。Bind 特性还有两个属性,Exclude 和 Include。它们可以指定在 Mdoel 的属性中,Binder 不查找或只查找某个属性,即在查找时要么只包含这个属性要么不包含这个属性。如下面的 action 方法:

  1. 1 public ActionResult DisplayAddress([Bind(Prefix = "HomeAddress", Exclude = "Country")]Address address)
  2. 2 {
  3. 3 return View(address);
  4. 4 }
复制代码

这时 Binder 在绑定时不会对 Address 这个 Model 的 Country 属性绑定值。



回复

使用道具 举报