日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > asp.net >内容正文

asp.net

精通ASP.NET MVC ——模型绑定

發布時間:2025/3/11 asp.net 60 豆豆
生活随笔 收集整理的這篇文章主要介紹了 精通ASP.NET MVC ——模型绑定 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

模型綁定(Model Binding)是指,用瀏覽器以Http請求方式發送的數據來創建.Net對象的過程。

準備示例項目?

新建一個空的MVC項目,名叫MvcModels,接下去會以此項目來演示各種功能。

Models文件夾中創建一個Person.cs類文件,代碼如下圖所示:

namespace MvcModels.Models {public class Person{public int PersonId { get; set; }public string FirstName { get; set; }public string LastName { get; set; }public DateTime BirthDate { get; set; }public Address HomeAddress { get; set; }public bool IsApproved { get; set; }public Role Role { get; set; }}public class Address{public string Line1 { get; set; }public string Line2 { get; set; }public string City { get; set; }public string PostalCode { get; set; }public string Country { get; set; }}public enum Role{Admin,User,Guest} }

定義一個Home控制器,代碼如下圖所示:

public class HomeController : Controller{private Person[] personData = {new Person { PersonId = 1,FirstName = "Adam",LastName = "Freeman" },new Person { PersonId = 2,FirstName = "Jacqui",LastName = "Griffyth"},new Person { PersonId = 3,FirstName = "John",LastName = "Smith" },new Person { PersonId = 4,FirstName = "Anne",LastName = "Jones"}};// GET: Homepublic ActionResult Index(int id){Person dataItem = personData.Where(p => p.PersonId == id).First();return View(dataItem);}}

新增Index控制器對應的Index.cshtml頁面,代碼如下:?

@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>Roles:</label>@Html.DisplayFor(m => m.Role)</div>

新增_layout.cshtml布局頁面,代碼如下:?

<!DOCTYPE html><html> <head><meta name="viewport" content="width=device-width" /><title>@ViewBag.Title</title><style>label {display:inline-block; width:100px;font-weight:bold;margin:5px;}form label {float:left;}input.text-box {float:left;margin:5px}button[type=submit] {margin-top:5px;float:left;clear:left;}form div {clear:both;}</style> </head> <body><div>@RenderBody()</div> </body> </html>

運行程序,并導航到/Home/Index/1,結果如下圖所示:?

? ? ? ? ? ? ? ?

默認的動作綁定器ControllerActionInvoker要依靠模型綁定器來生成調用動作所需的數據對象。模型綁定器IModelBinder接口所定義,接口如下圖所示:

public interface IModelBinder {object BindModel(ControllerContext controllerContext,ModelBindingContext bindingContext) }

在一個MVC應用程序中,可以有多個模型綁定器,而每個綁定器可以負責綁定一個或者多個模型類型。它會考察該方法所定義的參數,并查找各個參數類型所依賴的模型綁定器。

在上述示例中,動作調用器會檢查Index方法,并發現它具有一個int型的參數。于是會查找負責int值綁定的綁定器,并調用BindModel方法。


使用默認的模型綁定器

雖然程序可以定義自定義的模型綁定器,大多數程序都是依靠內建的模型綁定器DefaultModelBinder.當動作調用器找不到綁定某個類型的自定義綁定器時,這個默認的模型綁定器便是由動作調用器所使用的一個綁定器。默認情況下,這個模型綁定器會搜索四個位置:

DefaultModelBinder類查找參數數據的順序
描述
Request.Form由用戶在HTML的form(表單)元素中提供的值
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類。將已經從請求數據獲得的字符串值轉化成參數類型。如果無法轉為這個值:例如給int值傳一個“apple”,程序就會報錯:

解決這個問題有兩種辦法:

一、在動作方法參數中設置可空類型(nullable),這為綁定器提供一個退路,一個可空的int參數可以不必為數字值,這讓模型綁定器在調用動作時,這可以讓動作方法參數設置為Null。

public ActionResult Index(int? id)

二、 在動作方法中運用默認值,當模型綁定器無法為id參數找到一個值時,將默認值1來代替,如下所示:

public ActionResult Index(int id = 1)

?綁定復雜類型

當動作方法的參數是復合類型時,DefaultModelBinder類將用反射來獲取public屬性集。

在Home控制器中,新增如下兩個動作方法:?

public class HomeController : Controller{private Person[] personData = {new Person { PersonId = 1,FirstName = "Adam",LastName = "Freeman" },new Person { PersonId = 2,FirstName = "Jacqui",LastName = "Griffyth"},new Person { PersonId = 3,FirstName = "John",LastName = "Smith" },new Person { PersonId = 4,FirstName = "Anne",LastName = "Jones"}};// GET: Homepublic ActionResult Index(int? id = 1){Person dataItem = personData.Where(p => p.PersonId == id).First();return View(dataItem);}public ActionResult CreatePerson(){return View(new Person());}[HttpPost]public ActionResult CreatePerson(Person model){return View("Index",model);}}

為沒有參數的CreatePerson控制器方法創建一個對應的視圖:CreatePerson.cshtml?,代碼如下圖所示:

@model MvcModels.Models.Person @{ViewBag.Title = "CreatePerson";Layout = "~/Views/Shared/_Layout.cshtml"; }<h2>CreatePerson</h2> @using (Html.BeginForm()) {<div>@Html.LabelFor(m => m.PersonId) @Html.EditorFor(m => m.PersonId)</div><div>@Html.LabelFor(m => m.FirstName) @Html.EditorFor(m => m.FirstName)</div><div>@Html.LabelFor(m => m.LastName) @Html.EditorFor(m => m.LastName)</div><div>@Html.LabelFor(m => m.Role) @Html.EditorFor(m => m.Role)</div><button type="submit">Submit</button> }

運行導航到/Home/CreatePerson,結果如下圖所示:

? ? ? ? ??

點擊submit按鈕后,可以看到已經將輸入的數據? 傳到 Index 界面了:

? ? ? ? ?

?

在表單傳遞給CreatePerson方法時,形成了一種不同的模型綁定情況。默認的模型綁定器發現,動作方法需要一個Person對象,于是會依次處理每個屬性。?對于每個簡單類型的屬性,綁定器會視圖查找請求中的一個值,就如同上一個示例所做的那樣。因此,當遇到 PersonId屬性時,綁定器會查找personId的數據值,它將在請求的表單中發現這個值。

如果一個屬性需要另一個復合類型,那么,該過程會針對新類型重復執行。獲取該類型的public屬性集,而綁定器會視圖找出這些屬性的值。不同的是,這些屬性是嵌套的。例如,Person類的HomeAddress 屬性 是Address類型。


創建易于綁定的HTML

更新CreatePerson.cshtml中的代碼,以便為Address類型捕獲一些屬性:

@model MvcModels.Models.Person @{ViewBag.Title = "CreatePerson";Layout = "~/Views/Shared/_Layout.cshtml"; }<h2>CreatePerson</h2> @using (Html.BeginForm()) {<div>@Html.LabelFor(m => m.PersonId) @Html.EditorFor(m => m.PersonId)</div><div>@Html.LabelFor(m => m.FirstName) @Html.EditorFor(m => m.FirstName)</div><div>@Html.LabelFor(m => m.LastName) @Html.EditorFor(m => m.LastName)</div><div>@Html.LabelFor(m => m.Role) @Html.EditorFor(m => m.Role)</div><div>@Html.LabelFor(m => m.HomeAddress.City)@Html.EditorFor(m => m.HomeAddress.City)</div><div>@Html.LabelFor(m => m.HomeAddress.Country)@Html.EditorFor(m => m.HomeAddress.Country)</div><button type="submit">Submit</button> }

更新Index.html中的代碼,如下圖所示:?

@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>Roles:</label>@Html.DisplayFor(m => m.Role)</div><div><label>City:</label>@Html.DisplayFor(m => m.HomeAddress.City)</div> <div><label>County:</label>@Html.DisplayFor(m => m.HomeAddress.Country)</div>

運行導航到/Home/CreatePerson,如下圖所示:

? ? ? ? ? ?

點擊Submit按鈕后,數據傳遞成功,情況如下:

? ? ? ? ? ? ?

簡而言之,模型綁定器查找的是 HomeAddress.Country,即,模型對象的屬性名(HomeAddress)與屬性類型(Address)d的屬性名(Country)的組合。?


指定自定義前綴?

偶爾有些時候,生成的HTML與一種類型的對象有關,但是希望綁定到另一個對象。這意味著包含的前綴與模型綁定器期望的結構不對應。這個時候需要用到屬性注解了。

Models文件夾中創建了一個新的類文件,名稱為AddressSummary.cs,如下圖所示:

public class AddressSummary{public string City { get; set; }public string Country { get; set; }}

Home控制器中增加一個動作方法,如下圖所示:?

public ActionResult DisplaySummary(AddressSummary summary){return View(summary);}

修改CreatePerson.cshtml文件中表達提交的目標:

@model MvcModels.Models.Person @{ViewBag.Title = "CreatePerson";Layout = "~/Views/Shared/_Layout.cshtml"; }<h2>CreatePerson</h2> @using (Html.BeginForm("DisplaySummary","Home")) {<div>@Html.LabelFor(m => m.PersonId) @Html.EditorFor(m => m.PersonId)</div><div>@Html.LabelFor(m => m.FirstName) @Html.EditorFor(m => m.FirstName)</div><div>@Html.LabelFor(m => m.LastName) @Html.EditorFor(m => m.LastName)</div><div>@Html.LabelFor(m => m.Role) @Html.EditorFor(m => m.Role)</div><div>@Html.LabelFor(m => m.HomeAddress.City)@Html.EditorFor(m => m.HomeAddress.City)</div><div>@Html.LabelFor(m => m.HomeAddress.Country)@Html.EditorFor(m => m.HomeAddress.Country)</div><button type="submit">Submit</button> }

導航到/Home/CreatePerson方法:?

? ? ? ? ? ?

點擊submit方法后,結果如下圖所示:?

? ? ? ? ? ??

?由于Country和City的前綴改變了,由HomeAddress變成了AddressSummary,故綁定器無法實現綁定。只需對動作方法的參數運用Bind注解屬性即可,代碼如下圖所示:

public ActionResult DisplaySummary([Bind(Prefix ="HomeAddress")]AddressSummary summary) {return View(summary); }

重新運行代碼,即可看到運行成功:?

? ? ? ? ? ? ? ??


有選擇性的綁定屬性?

如果希望對某一屬性不需要模型綁定器進行綁定,可以使用如下代碼:

public ActionResult DisplaySummary([Bind(Prefix ="HomeAddress",Exclude ="Country")]AddressSummary summary) {return View(summary); }

也可以設置模型綁定器只綁定某一屬性,代碼如下圖所示:

[Bind(Include ="City")]public class AddressSummary{public string City { get; set; }public string Country { get; set; }}

?


綁定到數組

默認模型綁定器的一個雅致的特性是它支持動作方法參數作為數組。在Home控制器中添加一個方法,如下圖所示:

public ActionResult Names(string[] names){names = names ?? new string[0];return View(names);}

創建Names方法對應的視圖Names.csthml,如下圖所示:?

@model string[] @{ViewBag.Title = "Names";Layout = "~/Views/Shared/_Layout.cshtml"; }<h2>Names</h2> @if (Model.Length == 0) {using (Html.BeginForm()) {for (int i = 0; i < 3; i++){<div><label>@(i + 1):</label>@Html.TextBox("names")</div>}<button type="submit">Submit</button>} } else {foreach (string str in Model){<p>@str</p>}@Html.ActionLink("Back", "Names") }

導航到/Home/Names,如下圖所示:

? ? ? ? ? ?

點擊Submit后,如下圖所示:?

? ? ? ? ??

遞交表單時,默認的模型綁定器明白動作方法需要一個字符串數組。于是會查找與參數具有同樣名稱的數據項。在本例中,意味著會將所有input元素的內容聚集到一起用以填充數組。?

?

?


綁定到集合?

能綁定的不僅僅是數組,還可以使用.Net集合類。

修改names動作方法為強類型集合,如下圖所示:

public ActionResult Names(IList<string> names){names = names ?? new List<string>();return View(names);}

并修改Names.cshtml頁面代碼,如下所示:

@model IList<string> @{ViewBag.Title = "Names";Layout = "~/Views/Shared/_Layout.cshtml"; }<h2>Names</h2> @if (Model.Count() == 0) {using (Html.BeginForm()) {for (int i = 0; i < 3; i++){<div><label>@(i + 1):</label>@Html.TextBox("names")</div>}<button type="submit">Submit</button>} } else {foreach (string str in Model){<p>@str</p>}@Html.ActionLink("Back", "Names") }

運行結果如下圖所示:

? ? ? ? ? ??

點擊提交后:?


綁定到自定義模型集合?

可以將一些單個的數據屬性綁定成一個自定義類型的數組。如上述的AddressSummary模型類.

在控制器中增加一個新的動作方法,如下圖所示:

public ActionResult Address(IList<AddressSummary> addresses){addresses = addresses ?? new List<AddressSummary>();return View(addresses);}

添加Address動作方法對應的頁面,代碼如下圖所示:

@using MvcModels.Models @model IList<AddressSummary> @{ViewBag.Title = "Address";Layout = "~/Views/Shared/_Layout.cshtml"; }<h2>Address</h2> @if (Model.Count() == 0) {using (Html.BeginForm()){for (int i = 0; i < 3; i++){<fieldset><legend>Address @(i + 1)</legend><div><label>City:</label>@Html.Editor("[" + i + "].City")</div><div><label>Country:</label>@Html.Editor("[" + i + "].Country")</div></fieldset>}<button type="submit">Submit</button>} } else {foreach (AddressSummary str in Model){<p> @str.City, @str.Country</p>}@Html.ActionLink("Back","Address"); }

?導航到/Home/Address,并輸入內容,頁面如下圖所示:

? ? ? ? ? ? ? ?

點擊提交后,正確顯示頁面:

? ? ? ? ? ? ??

可以看到生成的HTML代碼:?

? ? ??

?

當表單被提交時,默認的模型綁定器知道它需要創建的是一個AddressSummary對象集合,并利用 name 標簽屬性中的數組索引前綴獲取對象的類型。以【0】為前綴的那些屬性表示一個AddressSummary對象,以【1】為前綴表示第二個對象。以此類推。?


手工調用模型綁定

當動作方法定義了參數時,模型綁定過程是自動執行的,但是只要你愿意,也可以直接控制這一過程。如下將演示如何將Home控制器的Address動作方法修改成手動調用綁定過程。

修改Address方法,代碼如下圖所示:

public ActionResult Address(IList<AddressSummary> addresses){addresses = addresses ?? new List<AddressSummary>();UpdateModel(addresses);return View(addresses);}

UpdataModel方法以上一條語句定義的時候的模型對象為參數,并試圖用標準的綁定該過程來獲取其public屬性的值。?

當手工調用綁定時,可以將綁定過程限制到單一的數據源。默認情況下,綁定器會搜索四個地方:表單數據、路由數據、查詢字符串,以及上傳文件。一下代碼演示了如何將綁定器限制到搜索單一位置的數據——表單數據。

public ActionResult Address(IList<AddressSummary> addresses){addresses = addresses ?? new List<AddressSummary>();UpdateModel(addresses,new FormValueProvider(ControllerContext));return View(addresses);}

UpdateModel方法的這一版本以IValueProvider接口的一個實現為參數,該實現也成為了綁定過程的唯一的數據源。四個默認的數據位置的每一個都由一個IValueProvider實現表示:

內建的IValueProvider
IValueProvider
Request.FormFormValueProvider
RouteData.ValueRouteDataValueProvider
Request.QueryStringQueryStringValueProvider
Request.FilesHttpFileCollectionValueProvider

可以用另一種跟優雅的寫法來表示:

public ActionResult Address(FormCollection formData){List<AddressSummary> addresses = new List<AddressSummary>();UpdateModel(addresses,formData);return View(addresses);}

?處理綁定錯誤

用戶難免會提供一些不能綁定到相應模型屬性的值,需要對這些情況拋出些異常。特別是使用UpdateModel方法時,必須做好捕捉該異常的準備,代碼如下圖所示:

public ActionResult Address(FormCollection formData){List<AddressSummary> addresses = new List<AddressSummary>();try{UpdateModel(addresses, formData);}catch (InvalidCastException ex){//給用戶提供反饋}return View(addresses);}

另一個可選的辦法是,可以使用TryUpdateModel方法。如果模型綁定成功,返回ture;否則返回false;

public ActionResult Address(FormCollection formData){List<AddressSummary> addresses = new List<AddressSummary>();if (TryUpdateModel(addresses,formData)){//正常處理}else{//給用戶提供反饋}return View(addresses);}

這兩種方式的唯一區別是,你是否喜歡捕捉并處理異常。?


定制模型綁定系統

還有一些不同的方式,可以對綁定系統進行定制。

通過定義一個自定義的值提供器,可以將自己的數據源添加到模型綁定過程。值提供器(Valueprovider)需要實現IValueProvider接口,如下圖所示:

public interface IValueProvider{bool ContainsPrefix(string prefix);ValueProviderResult GetValue(string key);}

ContainsPrefix方法由模型綁定器調用,以確定這個值提供器是否可以解析給定前綴的數據。

GetValue方法返回給定數據鍵的值,或者在提供器無法得到合適的數據時返回null。

新建一個CountryValueProvider類,實現以上接口:

public class CountryValueProvider : IValueProvider{public bool ContainsPrefix(string prefix){return prefix.ToLower().IndexOf("country") > -1;}public ValueProviderResult GetValue(string key){if (ContainsPrefix(key)){return new ValueProviderResult("USA", "USA", CultureInfo.InvariantCulture);}else{return null;}}}

該值提供器只對請求Country屬性的值進行響應,而且總是返回 USA?。對于其他請求,返回 NULL,表示無法提供數據。

返回值必須提供一個ValueProviderResult類來返回。這個類有三個構造器參數:第一個參數是與請求鍵關聯的數據項,第二個參數是作為HTML頁面一部分的該數據的安全顯示形式,第三個參數是該值相關的文化信息。這里已經指定為了InvariantCulture。

為了在應用程序中對這個值進行注冊,需要一個工廠類,以便在MVC框架需要時為這個提供器創建實例。這個工廠類必須派生于抽象類ValueProviderFactory。代碼如下圖所示:

public class CustomValueProviderFactory : System.Web.Mvc.ValueProviderFactory {public override IValueProvider GetValueProvider(ControllerContext controllerContext){return new CountryValueProvider();} }

?當模型綁定器要為綁定過程獲取值時,會調用這個GetValueProvider方法。上述實現了簡單的創建并返回了CurrentTimeProvider類的一個實例,但你可以使用ControllerContext參數提供的數據,以便創建不同的值提供器,對不同種類的請求進行響應。

然后在Global.asax的Application_Start方法中注冊:

protected void Application_Start(){AreaRegistration.RegisterAllAreas();RouteConfig.RegisterRoutes(RouteTable.Routes);ValueProviderFactories.Factories.Insert(0,new CustomValueProviderFactory());}

如果希望這一提供器在其他提供器不能提供數據值時作為一個備選,那么可以用Add方法把工廠追加到集合末尾:?

ValueProviderFactories.Factories.Add(new CustomValueProviderFactory());

運行程序,導航到/Home/Address,如下圖所示:

? ? ? ? ? ??

點擊Submit,如下圖所示:

? ? ? ? ? ??


?創建自定義模板綁定器

通過創建一個特定類型的自定義模型綁定器,可以覆蓋默認綁定器的行為。自定義模型綁定器需要實現IModelBinder接口。創建一個AddressSummaryBinder.cs類文件,如下圖所示:

public class AddressSummaryBinder : IModelBinder{public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext){AddressSummary model = (AddressSummary)bindingContext.Model ?? new AddressSummary();model.City = GetValue(bindingContext,"City");model.Country = GetValue(bindingContext,"Country");return model;}private string GetValue(ModelBindingContext context,string name){name = (context.ModelName == "" ? "" : context.ModelName + ".") + name;ValueProviderResult result = context.ValueProvider.GetValue(name);if (result == null || result.AttemptedValue == ""){return "<Not Specified>";}else{return (string)result.AttemptedValue;}}}

BindModel方法的參數是一個ControllerContext對象,可以用它來訪問當前請求的細節。另一個是ModelBindingContext對象,該對象提供了當前尋找的模型對象的細節,并能訪問MVC應用程序中其他模型綁定工具。?

ModelBindingContext類所定義的最有用的屬性
屬性描述
Model如果手工調用了綁定,可返回傳遞給UpdataModel方法的模型對象
ModelName返回被綁定模型的名稱
ModelType返回被創建模型的類型
ValueProvider返回能用于請求中獲得數據值的IValueProvider實現

在調用BindModel方法時,檢查已經是否設置了?ModelBindingContext 對象 的Model屬性,如果已經設置,則該模型便是將要為之生成數據值的對象,如沒有設置,則創建AddressSummary類的一個實例。通過調用GetValue方法獲取City和Country屬性的值,然后返回已經過填充的AddressSummary對象。

在GetValue方法中,通過了ModelBindingContext.ValueProvider屬性獲得的IValueProvider實現,以獲取模型對象屬性的值。

ModelName屬性能夠告訴我們,對正在尋找的屬性的名稱,是否需要追加一個前綴。當無法為一個屬性找到值,或者該屬性為空字符串時,便提供一個默認值<Not Specified>.

?

然后在Global.asax的Application_Start方法中注冊該模型綁定器:

protected void Application_Start(){AreaRegistration.RegisterAllAreas();RouteConfig.RegisterRoutes(RouteTable.Routes);ModelBinders.Binders.Add(typeof(AddressSummary), new AddressSummaryBinder());}

導航到/Home/Address,并輸入內容,如下圖所示:?

? ? ? ? ? ? ? ? ?

提交后,結構如下圖所示:?

? ? ? ? ? ? ? ? ? ?

?


?用注解屬性注冊模型綁定器

可以在模型類上使用ModelBinder注解屬性進行修飾,來注冊自定義模型綁定器。不必使用Global.asax文件。如下圖所示:

[ModelBinder(typeof(AddressSummaryBinder))]public class AddressSummary{public string City { get; set; }public string Country { get; set; }}

?

?

?

?

?

?

?

?

?

?

?

?

?

?

創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎

總結

以上是生活随笔為你收集整理的精通ASP.NET MVC ——模型绑定的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。