当前位置:编程学习 > asp >>

ModelBinder——ASP.NET MVC Model绑定的核心

Model的绑定体现在从当前请求提取相应的数据绑定到目标Action方法的参数。通过前面的介绍我们知道Action方法的参数通过ParameterDescriptor来描述,ParameterDescriptor的BindingInfo属性表示的ParameterBindingInfo对象具有一个名为ModelBinder的组件用于完成针对当前参数的Model绑定。ModelBinder可以看成是整个Model绑定系统的核心,我们先来认识这个重要的组件。
目录
一、 ModelBinder
二、CustomModelBinderAttribute与ModelBinderAttribute
三、ModelBinders
四、ModelBinderProvider
一、 ModelBinder
用于进行Model绑定的ModelBinder对象实现了接口IModelBinder。如下面的代码片断所示,IModelBinder接口具有唯一的BindModel方法用于实现针对某个参数的绑定操作,该方法的返回值表示的就是最终作为参数值的对象。
   1: public interface IModelBinder
   2: {
   3:     object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext);
   4: }
IModelBinder的BindModel方法接受两个参数,一个是表示当前的Controller上下文,另一个是表示针对当前Model绑定的上下文,通过类型ModelBindingContext表示。在Controller初始化的时候,Controller上下文已经被创建出来,所以我们只要能够针对当前的Model绑定创建相应的ModelBindingContext,我们就能使用基于某个参数的ModelBinder得到对应的参数值。关于ModelBindingContext的创建我们会在后续部分进行的单独介绍,我们先来介绍一下ModelBinder的提供机制。
二、CustomModelBinderAttribute与ModelBinderAttribute
如果针对某个参数的ParameterDescriptor具有相应的ModelBinder,那么它会被优先选择用于针对该参数的Model绑定,那么ParameterDescriptor的ModelBinder是如何来提供的呢?这是实际上设置一个具有如下定义的CustomModelBinderAttribute特性。抽象类CustomModelBinderAttribute定义了唯一的抽象方法GetBinder用于获取相应的ModelBinder对象。
   1: [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Interface | AttributeTargets.Enum | AttributeTargets.Struct
   2:     | AttributeTargets.Class, AllowMultiple=false, Inherited=false)]
   3: public abstract class CustomModelBinderAttribute : Attribute
   4: {
   5:     public abstract IModelBinder GetBinder();
   6: }
在ASP.NET MVC应用编程接口中,CustomModelBinderAttribute具有一个具有如下定义的唯一继承类型ModelBinderAttribute。我们可以通过应用ModelBinderAttribute特性动态地选择用于Model绑定的ModelBinder类型。
   1: [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Interface |
   2:     AttributeTargets.Enum | AttributeTargets.Struct | AttributeTargets.Class,
   3:     AllowMultiple=false, Inherited=false)]
   4: public sealed class ModelBinderAttribute : CustomModelBinderAttribute
   5: {  
   6:     public ModelBinderAttribute(Type binderType);
   7:     public override IModelBinder GetBinder();
   8: 
   9:     public Type BinderType { [CompilerGenerated] get;  }
  10: }
从应用在ModelBinderAttribute类型上的AttributeUsageAttribute定义可以看出该特性不仅仅可以应用在参数上,也可以应用类型(接口、枚举、结构和类)上,这意味我们既可以将它应用在Action方法的某个参数上,也可以将它应用在某个参数的类型上。但是ParameterDescriptor只会解析应用在参数上的特性,所以应用在参数对象类型上的ModelBinderAttribute对它是无效的。
为了演示ModelBinderAttribute特性对ParameterDescriptor的影响,我们来进行一个简单的实例演示。在一个通过Visual Studio的ASP.NET MVC项目模板创建的空Web应用中定义了如下几个类型,其中FooModelBinder和BarModelBinder是显现了IModelBinder的自定义ModelBinder类型,而Foo、Bar和Baz是三个将被作为Action方法参数的数据类型,其中Bar上应用了ModelBinderAttribute特性并将ModelBinder类型设置为BarModelBinder。
   1: public class FooModelBinder : IModelBinder
   2: {
   3:     public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
   4:     {
   5:         throw new NotImplementedException();
   6:     }
   7: }
   8: public class BarModelBinder : IModelBinder
   9: {
  10:     public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
  11:     {
  12:         throw new NotImplementedException();
  13:     }
  14: }
  15: 
  16: public class Foo { }
  17: [ModelBinder(typeof(BarModelBinder))]
  18: public class Bar { }
  19: public class Baz { }
然后再创建的默认HomeController中定义如下两个Action方法。DoSomething方法具有三个参数,类型分别是Foo、Bar和Baz,在第一个参数上应用了ModelBinderAttribute特性并将ModelBinder类型设置为FooModelBinder。
   1: public class HomeController : Controller
   2: {
   3:     public void Index()
   4:     {
   5:         ControllerDescriptor controllerDescriptor = new ReflectedControllerDescriptor(typeof(HomeController));
   6:         ActionDescriptor actionDescriptor = controllerDescriptor.FindAction(ControllerContext, "DoSomething");
   7:         IModelBinder foo = actionDescriptor.GetParameters().First(p => p.ParameterName == "foo").BindingInfo.Binder;
   8:         IModelBinder bar = actionDescriptor.GetParameters().First(p => p.ParameterName == "bar").BindingInfo.Binder;
   9:         IModelBinder baz = actionDescriptor.GetParameters().First(p => p.ParameterName == "baz").BindingInfo.Binder;
  10: 
  11:         Response.Write(string.Format("foo: {0}<br/>", null == foo? "N/A": foo.GetType().Name));
  12:         Response.Write(string.Format("bar: {0}<br/>", null == bar ? "N/A" : bar.GetType().Name));
  13:         Response.Write(string.Format("baz: {0}<br/>", null == baz ? "N/A" : baz.GetType().Name));
  14:     }
  15: 
  16:     public void DoSomething([ModelBinder(typeof(FooModelBinder))]Foo foo,Bar bar, Bar baz)
  17:     {}               
  18: }
在默认的Action方法Index中,我们针对HomeController类型的ReflectedControllerDescriptor对象并获取到用于描述Action方法DoSomething的ActionDescriptor对象。最后我们通过该ActionDescriptor对象得到用于描述其三个参数的ParameterDescriptor对象,并将其ModelBinder类西国内呈现出来。当我们运行该程序的时候,会在浏览器中产生如下的输出结果,可以看出对于分别应用在参数和参数类型上的ModelBinderAttribute特性,只有前者会对ParameterDescriptor的ModelBinder的选择造成影响。
   1: foo: FooModelBinder
   2: bar: N/A
   3: baz: N/A

三、ModelBinders
如果我们不曾通过ModelBinderAttribute特性为某个Action方法参数的ModelBinder类型进行显式定制,默认采用的Model是通过静态类型ModelBinders来提

补充:Web开发 , ASP.Net ,
CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,