一句代码实现批量数据绑定[下篇]
《上篇》主要介绍如何通过DataBinder实现批量的数据绑定,以及如何解决常见的数据绑定问题,比如数据的格式化。接下来,我们主要来谈谈DataBinder的设计,看看它是如何做到将作为数据源实体的属性值绑定到界面对应的控件上的。此外,需要特别说明一点:《上篇》中提供了DataBinder最初版本的下载,但已经和本篇文章介绍的已经大不一样了。最新版本的主要解决两个主要问题:通过Expression Tree的方式进行属性操作(属性赋值和取值),添加了“数据捕捉”(Data Capture)的功能,以实现将控件中的值赋给指定的实体。但是,这并不意味着这就是一个最终版本,这里面依然有一些问题,比如对空值的处理不不够全面,比如在进行数据绑定的时候,有的控件类型需要进行HTML Encoding,等等。[源代码从这里下载]
一、通过DataPropertyAttribute特性过滤实体的数据属性DataBinder在进行数据绑定的时候,并没有对作为数据源的对象作任何限制,也就是说任何类型的对象均可作为数据绑定的数据源。控件(这里指TextBox、Label等这样绑定标量数值的控件)绑定值来源于数据源实体的某个属性。但是一个类型的属性可能有很多,我们需要某种筛选机制将我们需要的“数据属性”提取出来。这里我们是通过在属性上应用DataPropertyAttribute一个特性来实现的。 简单起见,我不曾为DataPropertyAttribute定义任何属性成员。DataPropertyAttribute中定义了一个静态的GetDataProperties方法,得到给定实体类型的所有数据属性的名称。但是为了避免频繁地对相同实体类型进行反射,该方法对得到的属性名称数组进行了缓存。 1: [AttributeUsage( AttributeTargets.Property,AllowMultiple = false,Inherited = true)] 3: { 5: static string[] GetDataProperties(Type entityType) 7: Guard.ArgumentNotNullOrEmpty(entityType,128,1)">"entityType"); 9: { 11: } 13: { 16: 17: }
19: where property.GetCustomAttributes(typeof(DataPropertyAttribute),1)">true).Any() 21: dataProperties[entityType] = properties; 23: } 25: } 二、Control/DataSource映射的表示:BindingMapping不论是数据绑定(实体=〉控件),还是数据捕捉(控件=〉实体)的实现都建立在两种之间存在着某种约定的映射之上,这个映射是整个DataBinder的核心所在。在这里,我定义了如下一个BindingMapping类型表示这个映射关系。 2: { public Control Control { get; set; } 6: string DataSourceProperty { get; set; }
8: bool AutomaticUpdate { get; set; }
10: public Type ControlValuePropertyType
12: get { return PropertyAccessor.GetPropertyType(this.Control.GetType(),1)">this.ControlValueProperty); } 14: public Type DataSourcePropertyType
16: get { this.DataSourceType,1)">this.DataSourceProperty); }
18:? 20: { 22: this.DataSourceType = dataSourceType;
24: this.ControlValueProperty = controlValueProperty;
26: this.AutomaticBind = true; 28: } 30: { 32: } 34: { 36: bindingMapping.AutomaticBind = this.AutomaticBind;
38: return bindingMapping;
40: } 这里我主要介绍一下各个属性的含义:
需要补充一点的是:ControlValuePropertyType和DataSourcePropertyType使用到了之前定义的用于操作操作属性的组件ProcessAccessor。BindingMapping采用了克隆模式。 三、如何建立Control/DataSource映射集合BindingMapping表示的一个实体类型的数据属性和具体控件之间的映射关系,而这种关系在使用过程中是以批量的方式进行创建的。具体来说,我们通过指定实体类型和一个作为容器的空间,如果容器中的存在满足映射规则的子控件,相应的映射会被创建。映射的批量创建是通过DataBinder的静态方法BuildBindingMappings来实现的。 在具体介绍BuildBindingMappings方法之前,我们需要先来讨论一个相关的话题:在进行数据绑定的时候,如何决定数据应该赋值给控件的那个属性。我们知道,不同的控件类型拥有不同的数据绑定属性,比如TextBox自然是Text属性,CheckBox则是Checked属性。ASP.NET在定义控件类型的时候,采用了一个特殊性的特性ControlValuePropertyAttribute来表示那个属性表示的是控件的“值”。比如TextBox和CheckBox分别是这样定义的。 class TextBox : WebControl,IPostBackDataHandler,IEditableTextControl,ITextControl
5: } 7: ControlValueProperty("Checked")]
9: { string GetControlValuePropertyName(Control control) 4: { 6: } if (controlValueProperties.ContainsKey(entityType)) 10: return controlValueProperties[entityType];
12: typeof(DataBinder))
14: 15: {
18: ControlValuePropertyAttribute controlValuePropertyAttribute = (ControlValuePropertyAttribute)entityType.GetCustomAttributes(typeof(ControlValuePropertyAttribute),1)">true)[0];
20: return controlValuePropertyAttribute.Name;
22: } 最终的映射通过如下定义的BuildBindingMappings方法来建立,缺省参数suffix代表的是控件的后缀,其中已经在《上篇》介绍过了。 4: suffix = suffix??string.Empty;
6: let control = container.FindControl(string.Format("{1}{0}",suffix,property)) 8: where null != control 10: } 四、通过映射集合实现数据绑定通过《上篇》我们知道,DataBinder提供两种数据绑定方式:一种是直接通过传入数据实体对象和容器控件对具有匹配关系的所有子控件进行绑定;另外一种则是通过调用上面BuildBindingMappings静态方法建立的BindingMapping集合,然后再借助于这个集合进行数据绑定。这两种方式的数据绑定对应于如下两个重载的BindData方法: 2: {
protected virtual void OnBindData(IEnumerable<BindingMapping> bindingMappings,1)">object entity)5: var bindingMapping = mapping.Clone();7: null != this.DataItemBinding) 相关内容
|