首页 > 代码库 > 模型绑定(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类型为参数的动作方法.