首页 > 代码库 > 模型绑定(Model Binding)
模型绑定(Model Binding)
模型绑定是值用浏览器以HTTP请求方式发送数据来创建.NET对象的过程.(负责生成适当的动作方法参数值)
动作调用器(Action Invoker):调用控制器的动作方法的组件,负责在调用动作方法之前获取动作方法的参数值.
默认的动作调用器(ControllerActionInvoker)依赖于模型绑定器,而模型绑定器(Model Binder)是有IModelBinder接口定义的.
public interface IModelBinder { object BindModel(ControllerContext controllerContext,ModelBindingContext bindingContext); }
在MVC程序中,可以有多个模型绑定器,而每个绑定器可以负责绑定一个或者多个模型类型.当动作调用器需要调用一个动作方法时,它会考查这个方法所定义的参数,并查找各个参数所依赖的模型绑定器.
****模型绑定过程是通过模型绑定器来实现的,其目的是用请求中所包含的数据来创建对象.特别是在动作方法调用时,为动作方法创建参数对象.****
模型绑定的过程:
1.检查目标(需要创建的对象)的名称和类型.
2.根据第一步获取到的对象名称来查找请求,并从中找到数据(通常是字符串).
3.把第二步找的数据转换成目标类型.
4.通过对象的名称,对象的类型,和这种经过处理的数据来构建目标对象.
5.将构建好的对象送给动作调用器,并由动作调用机器将对象注入到目标的动作方法中去.
第二部分
虽然应用程序能有很多个模型绑定器,但是大多数是依赖内建的绑定器(DefaultModelBinder).
当动作调用器找不到类型对象的自定义绑定器时,它会调用默认绑定器.
默认绑定器(DefaultModelBinder)会在4个地方搜索数据.而且这个4个地方是有先后顺序的,只要在一个地方搜索到了数据就会立即返回该数据.不再搜索下个地方.
位置 | 描述 |
Request.Form | 用户在HTML的form(表单)中提供的数据 |
RouteData.Values | 用应用程序路由获得的值 |
Request.QueryString | 包含在请求URL的查询字符串部分的数据 |
Request.Files | 请求中的上传文件 |
简单类型绑定
当处理简单的参数类型时,DefaultModelBinder会尝试用System.ComponentModel.TypeDescriptor类把已经从请求中获得的字符串转换成动作方法的参数类型.
如果这个字符串不能转换目标类型,那么DefaultModelBinder便不能绑定.
如果想避免不能绑定这个情况的发生.有2种方法:
其一:可在定义动作方法时为参数提供可空类型(nullable).例如: public ViewResult ActionMethodName(int? id).
另一个:为动作方法的参数提供默认值.例如:public ViewResult ActionMethodName(int id=518).
绑定复合类型:
当动作方法参数是复合类型(基元类型)时,DefaultModelBinder类将使用反射来获取Public属性集,然后依次绑定.
简单过程:默认模型绑定器(DefaultModelBinder)会检查这个复合类型的各个Public属性,以便考察属性是否是简单的类型.如果是简单的类型,就会在请求中查找与该属性名称相同的数据项.(例如:Name属性,就是在请求的4个地方查找Name数据项).
这个有个问题就是,如果属性还是复合类型??怎么办??
这个情况下,该过程对新的复合类型重复执行,获取其Public属性集.而绑定器也会找出这个些属性的值,不同的是这些属性名是嵌套的.例如:一个复合类型A有一个属性的类型是复合类型B,而B复合类型有个简单类型属性C.那么在查找复合类型B的属性C时.模型绑定器查找的是B.C的的值,即模型对象属性的名称+属性类型的属性名称的组合.
绑定到数组与集合
默认的模型绑定器有一个特性===> 能处理具有相同名称的多个数据项的方式.
第三部分:手工调用模型绑定
当动作方法中定义了参数时,模型绑定过程是自动执行的.但是只要我们愿意,也可以直接控制这个过程.这样我们就能够更明确的控制如何实例化模型对象,从何处获取数据,以及如何处理数据解析错误.
手工调用模型绑定的过程
[HttpPost] public ActionResult ActionMethodName() { Person myPerson=new Person(); UpdateModel(myPerson); return View(myPerson); }
UpdateModel方法以上的一条语句生成的模型对象为参数,并试图用标准的过程来获得其public 属性的值,手工调用模型绑定过程的原因之一:支持模型对象的依赖注入(DI).
[HttpPost] public ActionResult ActionMethodName() { Person myPerson=(Person)DependencyResolver.Current.GetService(typeof(Person)); UpdateModel(myPerson); return View(myPerson); }
限制模型绑定器查找的位置===>默认的情况下,绑定器会到4个地方查找数据(Request.Form,RouteData.Values,Request.QueryString,Request.Files),我们手工调用绑定过程,可以把绑定过程限制到一个单一的位置.
[HttpPost] public ActionResult ActionMethodName() { Person myPerson=(Person)DependencyResolver.Current.GetService(typeof(Person)); // UpdateModel(myPerson); UpdateModel(myPerson,new FormValueProvider(ControllerContext)); return View(myPerson); }
UpdateModel方法的这个版本以IValueProvider接口的一个实现为参数,它成了绑定过程中的唯一数据源. 4个默认的数据位置都有一个IValueProvider实现.
4个数据源 | IValueProvider是实现 |
Request.Form | FormValueProvider |
RouteData.Values | RouteDataValueProvider |
Request.QueryString | QueryStringValueProvider |
Request.Filters | HttpFileCollectionValueProvider |
上面的每个实现都是用ControllerContext为构造函数的参数,我们可以通过Controller属性来得到它.
而现在数据源最普遍的方式只是查找表单值.我们可以用一个雅致的绑定技巧,而不必创建FormValueProvider的实例: FormCollection.
[HttpPost] public ActionResult ActionMethodName(FormCollection formData) { Person myPerson=(Person)DependencyResolver.Current.GetService(typeof(Person)); // UpdateModel(myPerson); // UpdateModel(myPerson,new FormValueProvider(ControllerContext)); UpdateModel(myPerson,formData) return View(myPerson); }
处理绑定错误
当手工调用绑定过程,我们需要负责处理任何错误.模型绑定器通过抛出InvalidOperationException异常来绑定错误..错误的具体细节通过ModelState特性来查看.
try { UpdateModel()//UpdateModel系列方法 }Catch(InvalidOperationException ex) { //....基于ModelState提供界面反馈 } if(TryUpdateModel()) { //.. } else { //....基于ModelState提供界面反馈 }
PS: 当自动调用模型绑定的过程时,绑定错误不会发出异常信号,我们必须通过ModelState.IsValid属性来检查结果.
使用模型绑定接受一个上传的文件: 我们要做的全部工作是:定义一个以HttpPostedFileBase类型为参数的动作方法.