ASP.NET MVC 4 (九) 模型绑定
模型绑定指的是MVC从浏览器发送的HTTP请求中为我们创建.NET对象,在HTTP请求和C#间起着桥梁的作用。模型绑定的一个最简单的例子是带参数的控制器action方法,比如我们注册这样的路径映射:
routes.MapRoute( name: "Default", url: "{controller}/{action}/{id}", defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional } );
控制器Home的Index action带有名为id的参数:
public ActionResult Index(int id) { Person dataItem = personData.Where(p => p.PersonId == id).First(); return View(dataItem); }
在我们请求URL“/Home/Index/1”时,默认action调用器ControllerActionInvoker使用模型绑定器为参数id赋值“1”。
默认模型绑定器
模型绑定器实现IModelBinder接口,MVC默认的模型绑定器类名为DefaultModelBinder。它从Request.form、RouteData.Values 、Request.QueryString、Request.Files查找参数值,比如上面例子中的参数id,它在下面路径中搜索:
- Request.Form["id"]
- RouteData.Values["id"]
- Request.QueryString["id"]
- Request.Files["id"]
模型绑定器使用参数的名称搜索可用值,一旦找到一个可以结果搜索即停止。
DefaultModelBinder在参数绑定中同时做类型变换,如果类型转换失败,参数绑定也失败,比如我们请求URL “/Home/Index/apple”会得到int类型不能null的错误,模型绑定器无法将apple转换成整数,视图将null赋值给id引发此错误。我们可以定义id参数为int?,这也只能解决部分问题,在Index方法内我们没有检查id为null的情况,我们可以使用默认参数来彻底解决:
... public ActionResult Index(int id = 1) { Person dataItem = personData.Where(p => p.PersonId == id).First(); return View(dataItem); } ...
实际的应用中我们还需要验证绑定的参数值,比如URL /Home/Index/-1和 /Home/Index/500都可以成功绑定数值到id,但他们超过了集合的上下限。在类型转换时还必须注意文化语言差异,比如日期格式,我们可以使用语言无关的通用格式yyyy-mm-dd。
复杂类型的绑定
上面我们看到的都是绑定到简单c#类型的例子,如果要绑定的模型是类则要复杂的多。以下面的Model类为例:
![](/assets/blank.gif)
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}
![](/assets/blank.gif)
创建两个CreatePerson控制器action来获取数据:
![](/assets/blank.gif)
public ActionResult CreatePerson() { return View(new Person()); } [HttpPost] public ActionResult CreatePerson(Person model) { return View("Index", model); }
![](/assets/blank.gif)
这里的action方法参数为复杂类型Person,我们使用Html.EditorFor()帮助函数在视图中创建输入数据的HTML:
![](/assets/blank.gif)
@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> }
![](/assets/blank.gif)
使用强类型的EditFor函数能保证生成的HTML元素Name包含模型绑定需要的嵌套前缀,比如HomeAddress.Country,生成的HTML为:
... <input class="text-box single-line" id="HomeAddress_Country" name="HomeAddress.Country" type="text" value="" /> ...
自定义绑定名称前缀
有这样一种情况,我们根据一个对象类型生成HTML,但是希望结果绑定到另外一个对象类型,我们可以通过自定义绑定前缀来实现。比如我们的Model类:
public class AddressSummary { public string City { get; set; } public string Country { get; set; } }
定义一个控制器方法来使用这个Model:
public ActionResult DisplaySummary(AddressSummary summary) { return View(summary); }
对应的DisplaySummary.cshtml视图也使用这个Model类:
![](/assets/blank.gif)
@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>
![](/assets/blank.gif)
如果我们从上面编辑Person的视图CreatePerson.cshtml提交到DisplaySummary action:
![](/assets/blank.gif)
@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> }
![](/assets/blank.gif)
DisplaySummary视图中将无法正确绑定City和Country,因为CreatePerson中City和Country的input元素名称包含HomeAddress前缀,提交的数据是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可以应用在单个action方法上,如果需要更大范围的效果,我们可以直接应用在模型类上:
[Bind(Include="City")]public class AddressSummary {public string City { get; set; }public string Country { get; set; }}
Bind可以同时应用在Model类和action方法上,一个属性只有在两个地方都没有被排除才会包含在绑定结果中。
绑定到数组和集合
DefaultModelBinder支持数组集合的绑定,比如下面的action方法使用数组作为参数:
public ActionResult Names(string[] names) { names = names ?? new string[0]; return View(names); }
视图中我们创建一组同名的input元素:
![](/assets/blank.gif)
@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"); }
![](/assets/blank.gif)
生成的HTML:
![](/assets/blank.gif)
... <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> ...
![](/assets/blank.gif)
提交数据时绑定器从多个names构建一个数组。
上面的例子换成集合是这样的:
public ActionResult Names(IList<string> names) {names = names ?? new List<string>();return View(names);}
视图:
![](/assets/blank.gif)
@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"); }
![](/assets/blank.gif)
如果是要绑定到一个自定义Model类型的集合:
public ActionResult Address(IList<AddressSummary> addresses) { addresses = addresses ?? new List<AddressSummary>(); return View(addresses); }
视图:
![](/assets/blank.gif)
@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"); }
![](/assets/blank.gif)
生成的HTML表单:
![](/assets/blank.gif)
... <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> ...
![](/assets/blank.gif)
使用[0]、[1]作为输入元素的名称前缀,绑定器知道需要创建一个集合。
手工调用模型绑定
在请求action方法时MVC自动为我们处理模型绑定,但是我们也可以在代码中手工绑定,这提供了额外的灵活性。我们调用控制器方法UpdateModel手工绑定:
public ActionResult Address() { IList<AddressSummary> addresses = new List<AddressSummary>(); UpdateModel(addresses); return View(addresses); }
我们可以提供UpdateModel额外的参数指定要数据提供者:
public ActionResult Address() { IList<AddressSummary> addresses = new List<AddressSummary>(); UpdateModel(addresses, new FormValueProvider(ControllerContext)); return View(addresses); }
参数FormValueProvider指定从Request.Form绑定数据,其他可用的Provider的还有RouteDataValueProvider(RouteData.Values)、QueryStringValueProvider(Request.QueryString)、HttpFileCollectionValueProvider(Request.Files),它们都实现IValueProvider接口,使用控制器类提供的ControllerContext作为构造函数参数。
实际上最常用的限制绑定源的方式是:
public ActionResult Address(FormCollection formData) { IList<AddressSummary> addresses = new List<AddressSummary>(); UpdateModel(addresses, formData); return View(addresses); }
FormCollection为表单数据的键值集合,这是UpdateModel众多重载形式中的一种。
手工数据绑定的另外一个好处是方便我们处理绑定错误:
![](/assets/blank.gif)
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); }
![](/assets/blank.gif)
另外一种处理错误的方式是使用TryUpdateModel:
![](/assets/blank.gif)
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); }
![](/assets/blank.gif)
自定义Value Provider
除了上面看到的内建Value provider,我们可以从IValueProvider接口实现自定义的Value provider:
namespace System.Web.Mvc { public interface IValueProvider { bool ContainsPrefix(string prefix); ValueProviderResult GetValue(string key); } }
模型绑定器调用ContainsPrefix方法确定value provider是否可以处理提供的名称前缀,GetValue根据传入的键返回可用的参数值,如果没有可用的数据返回null。下面用实例演示如何使用自定义value provider:
![](/assets/blank.gif)
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;}}}
![](/assets/blank.gif)
CountryValueProvider处理任何包含country的属性,对所有包含country名称的属性总是返回“USA”。使用自定义value provider之前还需要创建一个工厂类来创建自动那个有value provider的实例:
public class CustomValueProviderFactory : ValueProviderFactory {public override IValueProvider GetValueProvider(ControllerContext controllerContext) {return new CountryValueProvider();}}
最后把我们的类工厂在global.asax的application_start中添加到value provider工厂列表中:
![](/assets/blank.gif)
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);}}
![](/assets/blank.gif)
这里使用ValueProviderFactories.Factories.Insert()将自定义的value provider工厂添加到列表首位以优先使用,当然也可以ValueProviderFactories.Factories.Add()添加到列表末尾。在注册使用这个value provider后,任何对country属性的绑定都会得到值USA。
自定义模型绑定器
除了自定义value provider,我们还可以从IModelBinder接口创建自定义的模型绑定器:
![](/assets/blank.gif)
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;}}}
![](/assets/blank.gif)
MVC调用AddressSummaryBinder的BindModel()方法获取模型类型的实例,这里简单的初始化一个AddressSummary实例,调用value provider获取对象属性值,在从value provider获取属性值时我们把添加模型名称ModelBindingContext.ModelName作为属性的前缀。同样,必须在application_start中注册自定义模型绑定器后才能使用:
... ModelBinders.Binders.Add(typeof(AddressSummary), new AddressSummaryBinder()); ...
Dependency Injection和依赖解决器
C#中使用接口可以帮助我们解耦构件, 获取接口的实现我们通常是直接初始化接口的一个实现类:
![](/assets/blank.gif)
public class PasswordResetHelper { public void ResetPassword() { IEmailSender mySender = new MyEmailSender(); //...call interface methods to configure e-mail details... mySender.SendEmail(); } }
![](/assets/blank.gif)
使用IEmailSender接口在一定程度上PasswordResetHelper不再要求发送邮件时需要一个具体的邮件发送类,但是直接初始化MyEmailSender使得PasswordResetHelper并没有和MyEmailSender解耦开。我们可以把IEmailSender接口的初始化放到PasswordResetHelper的构造函数上来解决:
![](/assets/blank.gif)
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(); } }
![](/assets/blank.gif)
但这样带来的问题是如何获取IEmailSender的实现呢?这可以通过运行时Dependency Injection机制来解决,在创建PasswordResetHelper实例时依赖解决器提供一个IEmailSender的实例给PasswordResetHelper构造函数,这种注入方式又称为构造注入。依赖解决器又是怎么知道如何初始化接口的固实实现呢?答案是DI容器,通过在DI容器中注册接口/虚类和对应的实现类将两者联系起来。当然DI不只是DI容器这么简单,还必须考虑类型依赖链条、对象生命周期管理、构造函数参数配置等等问题,好在我们不需要编写自己的容器,微软提供自己的DI容器名为Unity(在nity.codeplex.com获取),而开源的Ninject是个不错的选择。Ninject可以在visual studio中使用nuget包管理器获取并安装,下面就以实例演示如何使用Ninject,我们从接口的定义开始:
![](/assets/blank.gif)
using System.Collections.Generic;namespace EssentialTools.Models {public interface IValueCalculator {decimal ValueProducts(IEnumerable<Product> products);} }
![](/assets/blank.gif)
接口的一个类实现:
![](/assets/blank.gif)
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));}} }
![](/assets/blank.gif)
我们创建一个使用Ninject的自定义依赖解决器:
![](/assets/blank.gif)
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>();}} }
![](/assets/blank.gif)
这里最重要的是AddBindings方法中的kernel.Bind<IValueCalculator>().To<LinqValueCalculator>(),它将接口IValueCalculator和类实现LinqValueCalculator结合起来,在我们需要接口IValueCalculator的一个实例时,会调用NinjectDependencyResolver的GetService获取到LinqValueCalculator的一个实例。要使NinjectDependencyResolver起作用还必须注册它为应用默认的依赖解决器,这是在application_start中操作:
![](/assets/blank.gif)
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);}}
![](/assets/blank.gif)
控制器的构造函数中我们传入接口IValueCalculator,依赖解决器会自动为我们创建一个LinqValueCalculator的实例:
![](/assets/blank.gif)
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); }}
![](/assets/blank.gif)
Ninject的绑定方法非常的灵活:
kernel.Bind<IDiscountHelper>().To<DefaultDiscountHelper>().WithPropertyValue("DiscountSize", 50M); //绑定时指定DefaultDiscountHelper的属性DiscountSize=50 kernel.Bind<IDiscountHelper>().To<DefaultDiscountHelper>().WithConstructorArgument("discountParam", 50M);//绑定时指定DefaultDiscountHelper的构造函数参数discountParam=50 kernel.Bind<IDiscountHelper>().To<FlexibleDiscountHelper>().WhenInjectedInto<LinqValueCalculator>();//条件绑定,在注入到LinqValueCalculator时绑定接口LinqValueCalculator到FlexibleDiscountHelper
除了使用自定义的依赖解决器,我们可以从默认控制器工厂扩展控制器工厂,在自定义控制器工厂中使用Ninject依赖注入:
![](/assets/blank.gif)
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>();}
![](/assets/blank.gif)
MVC在获取控制器时调用GetControllerInstance,它使用ninjectKernel.Get(controllerType)来获取相应的控制类实例,同时解决构造注入的问题,比如HomeController的构造函数参数IValueCalculator calcParam,使用这种方式可以限制仅在控制器内注入,控制器外整个应用范围内我们仍然可以使用自定义依赖解决器注入。
需要注意的是依赖解决和注入不是模型绑定的一部分,但它们有一定的相似性,后者解决的action方法上的参数绑定,前者可以说是整个控制器类(构造函数)上的参数绑定(当然不只是用在控制器类上)。
以上为对《Apress Pro ASP.NET MVC 4》第四版相关内容的总结,不详之处参见原版 http://www.apress.com/9781430242369。
转载于:https://www.cnblogs.com/sjqq/p/8241655.html
ASP.NET MVC 4 (九) 模型绑定相关推荐
- ASP.NET MVC中的模型装配 封装方法 非常好用
下面说一下 我们知道在asp.net mvc中 视图可以绑定一个实体模型 然后我们三层架构中也有一个model模型 但是这两个很多时候却是不一样的对象来的 就拿微软的官方mvc例子来说明 微软的视图实 ...
- 如何在FineUIMvc(ASP.NET MVC)视图中绑定多个模型?
起因 这是知识星球内的一个网友提出的,按理说ASP.NET MVC中一个视图只能绑定一个模型(Model),在视图顶部标识如下: @model IEnumerable<FineUICore.Ex ...
- ASP.NET MVC 4 (十) 模型验证
模型验证是在模型绑定时检查从HTTP请求接收的数据是否合规以保证数据的有效性,在收到无效数据时给出提示帮助用户纠正错误的数据. 显式模型验证 验证数据最直接的方式就是在action方法中对接收的数据验 ...
- 利用ASP.NET MVC 的默认类型绑定器---将Jquery datatables中的数据强类型绑定到实体类中
背景描述: 本文参考资料:https://blog.csdn.net/honantic/article/details/45913403 阅读了上述博文后对我产生了启发,在ASP.NET MVC 5中 ...
- [翻译:ASP.NET MVC 教程]理解模型、视图和控制器
本篇教程为你提供了ASP.NET MVC的模型.视图和控制器的高级概述.换句话说,即本文向你解释了在ASP.NET MVC中"M"."V"和"C&qu ...
- asp.net mvc请求响应模型原理回顾
根据讲师所讲总结了一下(可能存在些描述错误) -------------mvc进入asp.net管道原理: (在执行httpapplication管道之前mvc和asp.net是相同的,不同之处在于管 ...
- 《ASP.NET MVC 5 高级编程》 - 学习笔记
<ASP.NET MVC 5 高级编程> ========== ========== ========== [作者] (美) Jon Galloway (美) Brad Wilson (美 ...
- ASP.NET Core MVC 模型绑定用法及原理
前言 查询了一下关于 MVC 中的模型绑定,大部分都是关于如何使用的,以及模型绑定过程中的一些用法和概念,很少有关于模型绑定的内部机制实现的文章,本文就来讲解一下在 ASP.NET Core MVC ...
- [asp.net mvc 奇淫巧技] 04 - 你真的会用Action的模型绑定吗?
在QQ群或者一些程序的交流平台,经常会有人问:我怎么传一个数组在Action中接收.我传的数组为什么Action的model中接收不到.或者我在ajax的data中设置了一些数组,为什么后台还是接收不 ...
最新文章
- 解决Hbase启动报错问题:No such file or directory!
- Spring Cache 缺陷,我好像有解决方案了
- python求数组最大值_Python算法与数据结构--求所有子数组的和的最大值
- Excel-开发者工具(WPS)
- Dll入口函数参数详解
- 在Chrome开发者工具里观察到的SAP Spartacus productCodes
- 学习C# - Hello,World!
- 内存分析_Redis内存爆炸增长?你需要知道这一套Redis内存分析方法
- AWS re:Invent大会回顾
- oracle vm virtualbox安装xp系统,怎么使用VirtualBOX安装XP系统?VirtualBOX安装WinXp系统图文教程...
- Xshell上传文件
- Mac 安装Nessus
- pollard_rho算法
- Bert模型(一)安装及问题解决(基本每一步都有问题)
- 打印机服务器纸张属性不显示,为什么我的打印机能在打印机服务器属性里设置自定义纸张大小,却无法? 爱问知识人...
- 微信小程序实现图片文字识别提取
- 基于C++和OpenGL (GLUT) 实现太阳系行星系统
- 计算机培训通讯报道,新员工培训通讯稿3篇
- Linux中配置RAID磁盘阵列
- 勒索病毒-特洛伊木马变种
热门文章
- K8S部署工具:KubeOperator集群规划-手动模式
- 【完整代码】使用Semaphore实现限流代码示例
- Scala赋值运算符分类
- kibana操作elasticsearch:创建索引库
- 【视频】vue动态绑定css样式
- Lua与Redis交互
- MySQL搭建主从复制架构实战
- Sublime Text shift+ctrl妙用、Sublime Text快捷组合键大全
- 宁夏计算机专科大学排名,2019年民办学校排行榜_科普2019年宁夏专科学校排名及2019宁夏民办高校排...
- prometheus接入mysqld_exporter