這篇文章咱們來說講模型綁定(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 屬性綁定值。