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

歡迎訪問 生活随笔!

生活随笔

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

asp.net

ASP.NET MVC 4 (九) 模型绑定

發(fā)布時間:2024/10/12 asp.net 111 豆豆
生活随笔 收集整理的這篇文章主要介紹了 ASP.NET MVC 4 (九) 模型绑定 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

模型綁定指的是MVC從瀏覽器發(fā)送的HTTP請求中為我們創(chuàng)建.NET對象,在HTTP請求和C#間起著橋梁的作用。模型綁定的一個最簡單的例子是帶參數(shù)的控制器action方法,比如我們注冊這樣的路徑映射:

routes.MapRoute( name: "Default", url: "{controller}/{action}/{id}", defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional } );

控制器Home的Index action帶有名為id的參數(shù):

public ActionResult Index(int id) { Person dataItem = personData.Where(p => p.PersonId == id).First(); return View(dataItem); }

在我們請求URL“/Home/Index/1”時,默認action調(diào)用器ControllerActionInvoker使用模型綁定器為參數(shù)id賦值“1”。

默認模型綁定器

模型綁定器實現(xiàn)IModelBinder接口,MVC默認的模型綁定器類名為DefaultModelBinder。它從Request.form、RouteData.Values 、Request.QueryString、Request.Files查找參數(shù)值,比如上面例子中的參數(shù)id,它在下面路徑中搜索:

  • ?Request.Form["id"]
  • ?RouteData.Values["id"]
  • ?Request.QueryString["id"]
  • ?Request.Files["id"]
  • 模型綁定器使用參數(shù)的名稱搜索可用值,一旦找到一個可以結(jié)果搜索即停止。

    DefaultModelBinder在參數(shù)綁定中同時做類型變換,如果類型轉(zhuǎn)換失敗,參數(shù)綁定也失敗,比如我們請求URL “/Home/Index/apple”會得到int類型不能null的錯誤,模型綁定器無法將apple轉(zhuǎn)換成整數(shù),視圖將null賦值給id引發(fā)此錯誤。我們可以定義id參數(shù)為int?,這也只能解決部分問題,在Index方法內(nèi)我們沒有檢查id為null的情況,我們可以使用默認參數(shù)來徹底解決:

    ... public ActionResult Index(int id = 1) { Person dataItem = personData.Where(p => p.PersonId == id).First(); return View(dataItem); } ...

    實際的應(yīng)用中我們還需要驗證綁定的參數(shù)值,比如URL??/Home/Index/-1和?/Home/Index/500都可以成功綁定數(shù)值到id,但他們超過了集合的上下限。在類型轉(zhuǎn)換時還必須注意文化語言差異,比如日期格式,我們可以使用語言無關(guān)的通用格式y(tǒng)yyy-mm-dd。

    復(fù)雜類型的綁定

    上面我們看到的都是綁定到簡單c#類型的例子,如果要綁定的模型是類則要復(fù)雜的多。以下面的Model類為例:

    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}

    創(chuàng)建兩個CreatePerson控制器action來獲取數(shù)據(jù):

    public ActionResult CreatePerson() { return View(new Person()); } [HttpPost] public ActionResult CreatePerson(Person model) { return View("Index", model); }

    這里的action方法參數(shù)為復(fù)雜類型Person,我們使用Html.EditorFor()幫助函數(shù)在視圖中創(chuàng)建輸入數(shù)據(jù)的HTML:

    @model MvcModels.Models.Person @{ViewBag.Title = "CreatePerson"; } <h2>Create Person</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> }

    使用強類型的EditFor函數(shù)能保證生成的HTML元素Name包含模型綁定需要的嵌套前綴,比如HomeAddress.Country,生成的HTML為:

    ... <input class="text-box single-line" id="HomeAddress_Country" name="HomeAddress.Country" type="text" value="" /> ...

    自定義綁定名稱前綴

    有這樣一種情況,我們根據(jù)一個對象類型生成HTML,但是希望結(jié)果綁定到另外一個對象類型,我們可以通過自定義綁定前綴來實現(xiàn)。比如我們的Model類:

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

    定義一個控制器方法來使用這個Model:

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

    對應(yīng)的DisplaySummary.cshtml視圖也使用這個Model類:

    @model MvcModels.Models.AddressSummary @{ ViewBag.Title = "DisplaySummary"; } <h2>Address Summary</h2> <div><label>City:</label>@Html.DisplayFor(m => m.City)</div> <div><label>Country:</label>@Html.DisplayFor(m => m.Country)</div>

    如果我們從上面編輯Person的視圖CreatePerson.cshtml提交到DisplaySummary action:

    @model MvcModels.Models.Person @{ViewBag.Title = "CreatePerson"; } <h2>Create Person</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> }

    DisplaySummary視圖中將無法正確綁定City和Country,因為CreatePerson中City和Country的input元素名稱包含HomeAddress前綴,提交的數(shù)據(jù)是HomeAddress.City和HomeAddress.Country,而DisplaySummary視圖中是不需要這個前綴的。我們可以在控制器方法上通過Bind特性指定綁定前綴來修正:

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

    在Bind特性中我們還可以指定哪個屬性不要綁定,比如:

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

    這里通過Exclude="Country"禁止Country屬性的綁定,與此相對,可以通過Include來指定需要綁定的屬性。Bind可以應(yīng)用在單個action方法上,如果需要更大范圍的效果,我們可以直接應(yīng)用在模型類上:

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

    Bind可以同時應(yīng)用在Model類和action方法上,一個屬性只有在兩個地方都沒有被排除才會包含在綁定結(jié)果中。

    綁定到數(shù)組和集合

    DefaultModelBinder支持數(shù)組集合的綁定,比如下面的action方法使用數(shù)組作為參數(shù):

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

    視圖中我們創(chuàng)建一組同名的input元素:

    @model string[] @{ViewBag.Title = "Names"; } <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"); }

    生成的HTML:

    ... <form action="/Home/Names" method="post"> <div><label>1:</label><input id="names" name="names"type="text" value="" /></div> <div><label>2:</label><input id="names" name="names"type="text" value="" /></div> <div><label>3:</label><input id="names" name="names"type="text" value="" /></div> <button type="submit">Submit</button> </form> ...

    提交數(shù)據(jù)時綁定器從多個names構(gòu)建一個數(shù)組。

    上面的例子換成集合是這樣的:

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

    視圖:

    @model IList<string> @{ViewBag.Title = "Names"; } <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"); }

    如果是要綁定到一個自定義Model類型的集合:

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

    視圖:

    @using MvcModels.Models @model IList<AddressSummary> @{ViewBag.Title = "Address"; } <h2>Addresses</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"); }

    生成的HTML表單:

    ... <fieldset> <legend>Address 1</legend> <div> <label>City:</label> <input class="text-box single-line" name="[0].City"type="text" value="" /> </div> <div> <label>Country:</label> <input class="text-box single-line" name="[0].Country"type="text" value="" /> </div> </fieldset> <fieldset> <legend>Address 2</legend> <div> <label>City:</label> <input class="text-box single-line" name="[1].City"type="text" value="" /> </div> <div> <label>Country:</label> <input class="text-box single-line" name="[1].Country"type="text" value="" /> </div> </fieldset> ...

    使用[0]、[1]作為輸入元素的名稱前綴,綁定器知道需要創(chuàng)建一個集合。

    手工調(diào)用模型綁定

    在請求action方法時MVC自動為我們處理模型綁定,但是我們也可以在代碼中手工綁定,這提供了額外的靈活性。我們調(diào)用控制器方法UpdateModel手工綁定:

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

    我們可以提供UpdateModel額外的參數(shù)指定要數(shù)據(jù)提供者:

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

    ?參數(shù)FormValueProvider指定從Request.Form綁定數(shù)據(jù),其他可用的Provider的還有RouteDataValueProvider(RouteData.Values)、QueryStringValueProvider(Request.QueryString)、HttpFileCollectionValueProvider(Request.Files),它們都實現(xiàn)IValueProvider接口,使用控制器類提供的ControllerContext作為構(gòu)造函數(shù)參數(shù)。

    實際上最常用的限制綁定源的方式是:

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

    FormCollection為表單數(shù)據(jù)的鍵值集合,這是UpdateModel眾多重載形式中的一種。

    手工數(shù)據(jù)綁定的另外一個好處是方便我們處理綁定錯誤:

    public ActionResult Address(FormCollection formData) { IList<AddressSummary> addresses = new List<AddressSummary>(); try { UpdateModel(addresses, formData); } catch (InvalidOperationException ex) { // provide feedback to user } return View(addresses); }

    另外一種處理錯誤的方式是使用TryUpdateModel:

    public ActionResult Address(FormCollection formData) { IList<AddressSummary> addresses = new List<AddressSummary>(); if (TryUpdateModel(addresses, formData)) { // proceed as normal } else { // provide feedback to user } return View(addresses); }

    自定義Value Provider

    除了上面看到的內(nèi)建Value provider,我們可以從IValueProvider接口實現(xiàn)自定義的Value provider:

    namespace System.Web.Mvc { public interface IValueProvider { bool ContainsPrefix(string prefix); ValueProviderResult GetValue(string key); } }

    模型綁定器調(diào)用ContainsPrefix方法確定value provider是否可以處理提供的名稱前綴,GetValue根據(jù)傳入的鍵返回可用的參數(shù)值,如果沒有可用的數(shù)據(jù)返回null。下面用實例演示如何使用自定義value provider:

    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;}}}

    CountryValueProvider處理任何包含country的屬性,對所有包含country名稱的屬性總是返回“USA”。使用自定義value provider之前還需要創(chuàng)建一個工廠類來創(chuàng)建自動那個有value provider的實例:

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

    最后把我們的類工廠在global.asax的application_start中添加到value provider工廠列表中:

    public class MvcApplication : System.Web.HttpApplication {protected void Application_Start() {AreaRegistration.RegisterAllAreas();ValueProviderFactories.Factories.Insert(0, new CustomValueProviderFactory());WebApiConfig.Register(GlobalConfiguration.Configuration);FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);RouteConfig.RegisterRoutes(RouteTable.Routes);BundleConfig.RegisterBundles(BundleTable.Bundles);}}

    這里使用ValueProviderFactories.Factories.Insert()將自定義的value provider工廠添加到列表首位以優(yōu)先使用,當然也可以ValueProviderFactories.Factories.Add()添加到列表末尾。在注冊使用這個value provider后,任何對country屬性的綁定都會得到值USA。

    自定義模型綁定器

    除了自定義value provider,我們還可以從IModelBinder接口創(chuàng)建自定義的模型綁定器:

    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;}}}

    MVC調(diào)用AddressSummaryBinder的BindModel()方法獲取模型類型的實例,這里簡單的初始化一個AddressSummary實例,調(diào)用value provider獲取對象屬性值,在從value provider獲取屬性值時我們把添加模型名稱ModelBindingContext.ModelName作為屬性的前綴。同樣,必須在application_start中注冊自定義模型綁定器后才能使用:

    ... ModelBinders.Binders.Add(typeof(AddressSummary), new AddressSummaryBinder()); ...

    Dependency Injection和依賴解決器

    C#中使用接口可以幫助我們解耦構(gòu)件, 獲取接口的實現(xiàn)我們通常是直接初始化接口的一個實現(xiàn)類:

    public class PasswordResetHelper { public void ResetPassword() { IEmailSender mySender = new MyEmailSender(); //...call interface methods to configure e-mail details... mySender.SendEmail(); } }

    使用IEmailSender接口在一定程度上PasswordResetHelper不再要求發(fā)送郵件時需要一個具體的郵件發(fā)送類,但是直接初始化MyEmailSender使得PasswordResetHelper并沒有和MyEmailSender解耦開。我們可以把IEmailSender接口的初始化放到PasswordResetHelper的構(gòu)造函數(shù)上來解決:

    public class PasswordResetHelper { private IEmailSender emailSender; public PasswordResetHelper(IEmailSender emailSenderParam) { emailSender = emailSenderParam; } public void ResetPassword() { // ...call interface methods to configure e-mail details... emailSender.SendEmail(); } }

    但這樣帶來的問題是如何獲取IEmailSender的實現(xiàn)呢?這可以通過運行時Dependency Injection機制來解決,在創(chuàng)建PasswordResetHelper實例時依賴解決器提供一個IEmailSender的實例給PasswordResetHelper構(gòu)造函數(shù),這種注入方式又稱為構(gòu)造注入。依賴解決器又是怎么知道如何初始化接口的固實實現(xiàn)呢?答案是DI容器,通過在DI容器中注冊接口/虛類和對應(yīng)的實現(xiàn)類將兩者聯(lián)系起來。當然DI不只是DI容器這么簡單,還必須考慮類型依賴鏈條、對象生命周期管理、構(gòu)造函數(shù)參數(shù)配置等等問題,好在我們不需要編寫自己的容器,微軟提供自己的DI容器名為Unity(在nity.codeplex.com獲取),而開源的Ninject是個不錯的選擇。Ninject可以在visual studio中使用nuget包管理器獲取并安裝,下面就以實例演示如何使用Ninject,我們從接口的定義開始:

    using System.Collections.Generic;namespace EssentialTools.Models {public interface IValueCalculator {decimal ValueProducts(IEnumerable<Product> products);} }

    接口的一個類實現(xiàn):

    using System.Collections.Generic; using System.Linq;namespace EssentialTools.Models {public class LinqValueCalculator : IValueCalculator {private IDiscountHelper discounter;public LinqValueCalculator(IDiscountHelper discounterParam) {discounter = discounterParam;}public decimal ValueProducts(IEnumerable<Product> products) {return discounter.ApplyDiscount(products.Sum(p => p.Price));}} }

    我們創(chuàng)建一個使用Ninject的自定義依賴解決器:

    using System; using System.Collections.Generic; using System.Web.Mvc; using Ninject; using EssentialTools.Models;namespace EssentialTools.Infrastructure {public class NinjectDependencyResolver : IDependencyResolver {private IKernel kernel;public NinjectDependencyResolver() {kernel = new StandardKernel();AddBindings();}public object GetService(Type serviceType) {return kernel.TryGet(serviceType);}public IEnumerable<object> GetServices(Type serviceType) {return kernel.GetAll(serviceType);}private void AddBindings() {kernel.Bind<IValueCalculator>().To<LinqValueCalculator>();}} }

    這里最重要的是AddBindings方法中的kernel.Bind<IValueCalculator>().To<LinqValueCalculator>(),它將接口IValueCalculator和類實現(xiàn)LinqValueCalculator結(jié)合起來,在我們需要接口IValueCalculator的一個實例時,會調(diào)用NinjectDependencyResolver的GetService獲取到LinqValueCalculator的一個實例。要使NinjectDependencyResolver起作用還必須注冊它為應(yīng)用默認的依賴解決器,這是在application_start中操作:

    public class MvcApplication : System.Web.HttpApplication {protected void Application_Start() {AreaRegistration.RegisterAllAreas();DependencyResolver.SetResolver(new NinjectDependencyResolver());WebApiConfig.Register(GlobalConfiguration.Configuration);FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);RouteConfig.RegisterRoutes(RouteTable.Routes);}}

    控制器的構(gòu)造函數(shù)中我們傳入接口IValueCalculator,依賴解決器會自動為我們創(chuàng)建一個LinqValueCalculator的實例:

    public class HomeController : Controller {private Product[] products = {new Product {Name = "Kayak", Category = "Watersports", Price = 275M},new Product {Name = "Lifejacket", Category = "Watersports", Price = 48.95M},new Product {Name = "Soccer ball", Category = "Soccer", Price = 19.50M},new Product {Name = "Corner flag", Category = "Soccer", Price = 34.95M}};private IValueCalculator calc;public HomeController(IValueCalculator calcParam) {calc = calcParam;}public ActionResult Index() {ShoppingCart cart = new ShoppingCart(calc) { Products = products };decimal totalValue = cart.CalculateProductTotal();return View(totalValue); }}

    ?Ninject的綁定方法非常的靈活:

    kernel.Bind<IDiscountHelper>().To<DefaultDiscountHelper>().WithPropertyValue("DiscountSize", 50M); //綁定時指定DefaultDiscountHelper的屬性DiscountSize=50 kernel.Bind<IDiscountHelper>().To<DefaultDiscountHelper>().WithConstructorArgument("discountParam", 50M);//綁定時指定DefaultDiscountHelper的構(gòu)造函數(shù)參數(shù)discountParam=50 kernel.Bind<IDiscountHelper>().To<FlexibleDiscountHelper>().WhenInjectedInto<LinqValueCalculator>();//條件綁定,在注入到LinqValueCalculator時綁定接口LinqValueCalculator到FlexibleDiscountHelper

    除了使用自定義的依賴解決器,我們可以從默認控制器工廠擴展控制器工廠,在自定義控制器工廠中使用Ninject依賴注入:

    public class NinjectControllerFactory : DefaultControllerFactory {private IKernel ninjectKernel;public NinjectControllerFactory() {ninjectKernel = new StandardKernel();AddBindings();}protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType) {return controllerType == null? null: (IController)ninjectKernel.Get(controllerType);}private void AddBindings() {ninjectKernel.Bind<IValueCalculator>().To<LinqValueCalculator>();}

    MVC在獲取控制器時調(diào)用GetControllerInstance,它使用ninjectKernel.Get(controllerType)來獲取相應(yīng)的控制類實例,同時解決構(gòu)造注入的問題,比如HomeController的構(gòu)造函數(shù)參數(shù)IValueCalculator calcParam,使用這種方式可以限制僅在控制器內(nèi)注入,控制器外整個應(yīng)用范圍內(nèi)我們?nèi)匀豢梢允褂米远x依賴解決器注入。

    需要注意的是依賴解決和注入不是模型綁定的一部分,但它們有一定的相似性,后者解決的action方法上的參數(shù)綁定,前者可以說是整個控制器類(構(gòu)造函數(shù))上的參數(shù)綁定(當然不只是用在控制器類上)。

    ?

    以上為對《Apress Pro ASP.NET MVC 4》第四版相關(guān)內(nèi)容的總結(jié),不詳之處參見原版?http://www.apress.com/9781430242369。?

    ?

    轉(zhuǎn)載于:https://www.cnblogs.com/sjqq/p/8241655.html

    總結(jié)

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

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。