Asp.Net Mvc Beta新特性之自动绑定(2)-深入探索篇

 

在上篇中介绍了自动绑定的基本用法,在本篇中,我们将深入了解自动绑定的工作原理.
自动绑定的确是让人感到兴奋的特性,然而,为了让它可以在我们的项目中更好的工作,我们有必要深入了解如何更进一步细调该特性以及它是如何工作的.而本文正式即将揭开这个谜底.
为了更好的了解该特性,我们有必要到codeplex去下载一份asp.net mvc的源代码并分析之,在本文写作的时候,codeplex上已经放上了beta版的源码,如果想进一步了解的朋友可以下载并对照本文分析.
在beta版中,新增了自动绑定这一特性,并对绑定特性做了一定的修改
1.      新增BindAttribute:自动绑定特性设置
2.      修改DefaultModelBinder:自动绑定的实现部分
3.      修改ControllerActionInvoker:绑定的调用入口
4.      新增ModelBinderContext,封装绑定所需数据
5.      新增BinderResult,封装绑定结果
其他不大重要的修改略过
       我们一步步来分析绑定的执行过程,首先肯定在ControllerActionInvoker中,看到GetParameterValue方法:


这儿便是对每个参数都尝试调用ModelBinder来绑定参数,这儿的GetModelBinder方法和P5的一样,在我们自定义ModelBinder的情况下可以进行自定义绑定,然而在自动绑定的时候获取的则是DefaultModelBinder,然后在GetPropertyFilter方法中通过查阅BindAttribute来获取关于绑定的设置.最后对数据进行绑定.
       关键的,我们需要对DefaultModelBinder进行分析,然而在此之前,还有一个类也是需要我们仔细看看的,那就是BindAttribute,该特性是用来修饰参数的,它有4个重要的属性:Include,Exclude,Prefix和一个方法:IsPropertyAllowed,分别用来设定:绑定的字段,不绑定的字段,参数前缀和判断给定的字段是否设定运行绑定,且该方法会作为一个Predicate<string>委托封入ModelBinderContext传入BindModel方法.
       现在来讨论使用默认绑定的情况,首先给出DefaultModelBinder的所有方法:


分别简介下这些方法的作用:
1.      BindModel:对外的调用接口,根据传入的ModelBinderContext绑定值
2.      BindModelCore:绑定自定义类型,自定义类型数组或者自定义类型字典
3.      BindProperty:绑定某个指定的属性,(此处是一个递归调用,仍然调用DefaultModelBinder进行属性的绑定,也就是说,理论上DefaultModelBinder可以对任意深度的属性进行绑定)
4.      ConvertSimpleArrayType,ConvertSimpleType,用来做类型转换,一个转换数组一个转换普通类型
5.      CreateArray:创建一个数组对象
6.      CreateModel:创建一个普通对象
7.      CreateSubIndexName:创建子索引名,命名方式为prefix[indexName]
8.      CreateSubPropertyName:创建子属性名,命名方式为prefix.propertyName
9.      GetBinder:获取modelType的Binder对象
10.    GetElementType:获取一个类型的ElementType
11.    GetSimeType:对给定的ModelBinderContext进行简单值绑定(也就是调用该方法时假定获取的数据是简单类型)
12.    IsCollectionInserface:判断是否是数组类型
13.    IsDictinaryInterface:判断是否是字典类型
14.    IsSimpleType:是否是简单类型(在这儿判断是否是值类型或者string)
15.    TryUpdateSimpleCollection:尝试绑定一个简单数组(即绑定Collection或者Collection<T>,且T能通过IsSimpleType)
16.    UpdateCollection:绑定一个数组,该数组一般为Collection<T>,且T是自定义类型
17.    UpdateDictionary:绑定一个字典类型
 
通过以上方法的浏览,我们发现,DefaultModelBinder可以进行绑定的数据很多,包括简单类型(值类型和string).自定义类型,数组,字典,同时由于采用了递归调用,理论上可以绑定任意深度的数据,由于这儿绑定的调用比较复杂,且本人表达能力有限,本文将不再详细讲述绑定的工作流程,下面将总结下绑定的规则:
1.      默认绑定采用反射的方式将数据绑定到所需对象上
2.      绑定参数有可选特性BindAttribute定义
3.      BindAttribute中可以手动设置允许绑定的属性或者不允许绑定的属性,url取参前缀
4.      默认情况下所有属性都进行绑定
5.      DefaultModelBinder采用递归对所有需要绑定的参数进行绑定
6.      普通参数的命名方式为:prefix.protertyName,且默认情况下prefix为类型名.如对象MyData拥有类型为string的属性Name,那么在ValueProvider中取值的名称应该为mydata.name,命名不分大小写
7.      简单数组命名方式为prefix.protertyName,其中proertyName为数组名,可以存在多个
8.      对于自定义类型数组和字典,绑定必须提供如下参数:
        i.        index-必须提供一个或多个,指示绑定的子元素名,可存在多个
       ii.        数组必须提供一个或者多个以protertyName[indexName].subProtertyName形式结尾的表单数据,其中protertyName为数组名,indexName为index中定义过的名称,subProteryName为自定义类型的属性名.
      iii.        字典必须提供一个或者多个protertyName[indexName].key和protertyName[indexName].value的表单数据,其中protertyName为字典名称,indexName为index中定义过的名称,key代表字典中的key绑定,value代表字典中的value绑定,如果key或者value为非简单类型,表单的定义继续参照前面.
9.      对于以上绑定,如果子级属性为复杂类型,可以依次按照此规则命名表单数据
下面我们通过例子来分别描述上面的规则:
       在体验篇中,我们看到对article的表单命名为article.title,article.content等,这就是根据第6条,父类和属性用.来间隔,对简单数据的命名,article.tags,多个同名表单代表多个数族元素,参考第7条,在这儿,如果在article参数中显式定义了prefix,则需要对应对表单名进行修改.对于属性为自定义类型的,继续使用.间隔,比如上文中的article.advancearticle.today等,此处参考第7,9条,这些部分的例子可以参考第一篇,本篇中将不再说明.
       本篇中将重点介绍自定义类型数组,字典以及多级属性绑定,下面展示如何绑定一个ICollection<T>,T为自定义类型,比如在AdvanceArticle中添加一个属性Reads,这是一个自定义类型,包含Name和Source,实例代码如下;


然后根据规则在表单aspx中加入:


这儿给这个数组传入了两个元素,分别对应article.reads.index中的0和aa,下面的article.reads[0].name和article.reads[0].source表示第一个元素的子属性,依次类推.如果您在应用中需要在数组中添加更多元素,可以采用js动态添加表单域的方法,不过每次添加必须对应添加index和对应的子属性表单.
然后是绑定Dictinary<TKey,TValue>,我们在AdvanceArticle中继续加入新属性:


然后在aspx中加入表单:


和数组类似的,需要定义article.source.index,此处也显式定义了两个字典元素,分别是user和vip(注意,这儿的user和vip并不代表字典key,字典key是需要从表单绑定的),然后分别定义article.source[user].key和article.source[user].key等.如果有更多的元素,也需要按照此规则加入.
同样的,这儿的key和value也不局限于简单类型,如果key和value是自定义类型,则可以按照前面的规则继续向下定义.
最后需要说明几点:
1.     绑定取值不限于表单域,在Beta中加入了ValueProvider,只要是能在ValueProvider中能取到的值都能绑定.
2.     自动绑定是基于反射,如果你嫌效率不够高,请采用自定义ModelBinder的方式对特定的类型进行特定的绑定已提升效率
3.     自动绑是单向的,如果想实现在route中能接受该参数,可以采用重写绑定对象的ToString方法输出对应QueryString的方式,也就是说,如果想在调用Url.Action(“xxx”,new { article= myArticle })能正确传入传出参数,可以重写Article的ToString方法,在提供一个类似article.title=xxx&article.content=xxx的字符串格式提供一个帮助类,将将数据分别放入ValueProvider,而调用Url方法时采用类似Url.Action(“xxx”,article.ToRouteValue())的方法.
By Leven
2008-10-22
文章来自: 本站原创
引用通告: 查看所有引用 | 我要引用此文章
Tags: asp.net  asp.net mvc  .net  c#  绑定 
评论: 0 | 引用: 0 | 查看次数: 535
发表评论
用户名:
密 码: 游客发言不需要密码.
验证码: 验证码
内 容:
选 项:
虽然发表评论不用注册,但是为了保护您的发言权,建议您注册帐号.
字数限制 500 字 | HTML代码允许 关闭 | 评论可修改 关闭