一“.NET研究”句代码实现批量数据绑定[下篇]

简介:   《上篇》主要介绍如何通过DataBinder实现批量的数据绑定,以及如何解决常见的数据绑定问题,比如数据的格式化。接下来,我们主要来谈谈DataBinder的设计,看看它是如何做到将作为数据源实体的属性值绑定到界面对应的控件上的。

  《上篇》主要介绍如何通过DataBinder实现批量的数据绑定,以及如何解决常见的数据绑定问题,比如数据的格式化。接下来,我们主要来谈谈DataBinder的设计,看看它是如何做到将作为数据源实体的属性值绑定到界面对应的控件上的。此外,需要特别说明一点:《上篇》中提供了DataBinder最初版本的下载,但已经和本篇文章介绍的已经大不一样了。

  最新版本的主要解决两个主要问题:通过Expression Tree的方式进行属性操作(属性赋值和取值),添加了“数据捕捉”(Data Capture)的功能,以实现将控件中的值赋给指定的实体。但是,这并不意味着这就是一个最终版本,这里面依然有一些问题,比如对空值的处理不不够全面,比如在进行数据绑定的时候,有的控件类型需要进行HTML Encoding,等等。[源代码从这里下载]

目录:
一、通过DataPropertyAttribute特性过滤实体的“数据属性”
二、Control/DataSource映射的表示:BindingMapping
三、如何建立Control/DataSource映射集合
四、通过映射集合实现数据绑定
五、通过映射集合实现数据捕捉

  一、通过DataPropertyAttribute特性过滤实体的数据属性

  DataBinder在进行数据绑定的时候,并没有对作为数据上海企业网站设计与制作源的对象作任何限制,也就是说任何类型的对象均可作为数据绑定的数据源。控件(这里指TextBox、Label等这样绑定标量数值的控件)绑定值来源于数据源实体的某个属性。但是一个类型的属性可能有很多,我们需要某种筛选机制将我们需要的“数据属性”提取出来。这里我们是通过在属性上应用DataPropertyAttribute一个特性来实现的。

  简单起见,我不曾为DataPropertyAttribute定义任何属性成员。DataPropertyAttribute中定义了一个静态的GetDataProperties方法,得到给定实体类型的所有数据属性的名称。但是为了避免频繁地对相同实体类型进行反射,该方法对得到的属性名称数组进行了缓存。

 
 
[AttributeUsage( AttributeTargets.Property, AllowMultiple = false ,Inherited = true )]
public class DataPropertyAttribute: Attribute
{
private static Dictionary < Type, string [] > dataProperties = new Dictionary < Type, string [] > ();
public static string [] GetDataProperties(Type entityType)
{
Guard.ArgumentNotNullOrEmpty(entityType,
" entityType " );
if (dataProperties.ContainsKey(entityType))
{
return dataProperties[entityType];
}
lock ( typeof (DataPropertyAttribute))
{
if (dataProperties.ContainsKey(entityType))
{
return dataProperties[entityType];
}
var properties
= (from property in entityType.GetProperties()
where property.GetCustomAttributes( typeof (DataPropertyAttribute), true ).Any()
select property.Name).ToArray();
dataProperties[entityType]
= properties;
return properties;
}
}
}

  二、Control/DataSource映射的表示:BindingMapping

  不论是数据绑定(实体=〉控件),还是数据捕捉(控件=〉实体)的实现都建立在两种之间存在着某种约定的映射之上,这个映射是整个DataBinder的核心所在。在这里,我定义了如下一个BindingMapping类型表示这个映射关系。

 
 
public class BindingMapping: ICloneable
{
public Type DataSourceType { get ; private set ; }
public Control Control { get ; set ; }
public string ControlValueProperty { get ; set ; }
public string DataSourceProperty { get ; set ; }
public 上海徐汇企业网站制作> bool AutomaticBind { get ; set ; }
public bool AutomaticUpdate { get ; set ; }
public string FormatString { get ; set ; }
public Type ControlValuePropertyType
{
get { return PropertyAccessor.GetPropertyType( this .Control.GetType(), this .ControlValueProperty); }
}
public Type DataSourcePropertyType
{
get { return PropertyAccessor.GetPropertyType( this .DataSourceType, this .DataSourceProperty); }
}

public BindingMapping(Type dataSourceType, Control control, string controlValueProperty, string dataSourceProperty)
{
// ...
this .DataSourceType = dataSourceType;
this .Control = control;
this .ControlValueProperty = controlValueProperty;
this .DataSourceProperty = dataSourceProperty;
this .AutomaticBind = true ;
this 上海闵行企业网站制作 .AutomaticUpdate = true ;
}
object ICloneable.Clone()
{
return this .Clone();
}
public BindingMapping Clone()
{
var bindingMapping
= new BindingMapping( this .DataSourceType, this .Control, this .ControlValueProperty, this .DataSourceProperty);
bindingMapping.AutomaticBind
= this .AutomaticBind;
bindingMapping.AutomaticUpdate
= this .AutomaticBind;
return bindingMapping;
}
}

  这里我主要介绍一下各个属性的含义:

  • DataSourceType:作为数据源实体的类型;
  • Control:需要绑定的控件;
  • ControlValueProperty:数据需要绑定到控件属性的名称,比如TextBox是Text属性,而RadioButtonList则是SelectedValue属性;
  • DataSourceProperty:实体类型中的数据属性名称
  • AutomaticBind:是否需要进行自动绑定,通过它阻止不必要的自动数据绑定行为。默认值为True,如果改成False,基于该条映射的绑定将被忽略;
  • AutomaticUpdate:是否需要进行自动更新到数据实体中,通过它阻止不必要的自动数据捕捉行为。默认值为True,如果改成False,基于该条映射的数据捕捉定将被忽略;
  • FormatString:格式化字符串;
  • ControlValuePropertyType:控件绑定属性的类型,比如TextBox的绑定属性为Text,那么ControlValuePropertyType为System.String;
  • DataSourcePropertyType:实体属性类型。

  需要补充一点的是:ControlValuePropertyType和DataSourcePropertyType使用到了之前定义的用于操作操作属性的组件ProcessAccessor。BindingMapping采用了克隆模式。

  三、如何建立Control/DataSource映射集合

  BindingMapping表示的一个实体类型的数据属性和具体控件之间的映射关系,而这种关系在使用过程中是以批量的方式进行创建的。具体来说,我们通过指定实体类型和一个作为容器的空间,如果容器中的存在满足映射规则的子控件,相应的映射会被创建。映射的批量创建是通过DataBinder的静态方法BuildBindingMappings来实现的。

  在具体介绍BuildBindingMappings方法之前,我们需要先来讨论一个相关的话题:在进行数据绑定的时候,如何决定数据应该赋值给控件的那个属性。我们知道,不同的控件类型拥有不同的数据绑定属性,比如TextBox自然是Text属性,CheckBox则是Checked属性。ASP.NET在定义控件类型的时候,采用了一个特殊性的特性ControlValuePropertyAttribute来表示那个属性表示的是控件的“值”。比如TextBox和CheckBox分别是这样定义的。

 
 
[ControlValueProperty( " Text " )]
public class TextBox : WebControl, IPostBackDataHandler, IEditableTextControl, ITextControl
{
// ...
}

ControlValueProperty(
" Checked " )]
public class CheckBox : WebControl, IPostBackDataHandler, ICheckBoxControl
{
// ...
}

  在这里我们直接将ControlValuePropertyAttribute中指定的名称作为控件绑定的属性名,即BindingMapping的ControlValueProperty属性。该值得获取通过如下一个GetControlValuePropertyName私有方法完成。为了避免重复反射操作,这里采用了全局缓存。

 
 
private static string GetControlValuePropertyName(Control control)
{
if ( null == control)
{
return null ;
}
Type entityType
上海网站建设tyle="color: #000000;">= control.GetType();
if (controlValueProperties.ContainsKey(entityType))
{
return controlValueProperties[entityType];
}
lock ( typeof (DataBinder))
{
if (controlValueProperties.ContainsKey(entityType))
{
return controlValueProperties[entityType];
}
ControlValuePropertyAttribute controlValuePropertyAttribute
= (ControlValuePropertyAttribute)entityType.GetCustomAttributes( typeof (ControlValuePropertyAttribute), true )[ 0 ];
controlValueProperties[entityType]
= controlValuePropertyAttribute.Name;
return controlValuePropertyAttribute.Name;
}
}
上海企业网站制作

  最终的映射通过如下定义的BuildBindingMappings方法来建立,缺省参数suffix代表的是控件的后缀,其中已经在《上篇》介绍过了。

 
 
public static IEnumerable < BindingMapping > BuildBindingMappings(Type entityType, Control container, string suffix = "" )
{
// ...
suffix = suffix ?? string .Empty;
return (from property in DataPropertyAttribute.GetDataProperties(entityType)
let control
= container.FindControl( string .Format( " {1}{0} " , suffix, property))
let controlValueProperty
= GetControlValuePropertyName(control)
where null != control
select
new BindingMapping(entityType, control, controlValueProperty, property)).ToArray();
}

  四、通过映射集合实现数据绑定

  通过《上篇》我们知道,DataBinder提供两种数据绑定方式:一种是直接通过传入数据实体对象和容器控件对具有匹配关系的所有子控件进行绑定;另外一种则是通过调用上面BuildBindingMappings静态方法建立的BindingMapping集合,然后再借助于这个集合进行数据绑定。这两种方式的数据绑定对应于如下两个重载的BindData方法:

 
 
public class DataBinder
{
// ...
public void BindData( object entity, Control container, string suffix = "" );
public void BindData( object entity,IEnumerable < BindingMapping > bindingMappings);
}

  已经上在内部,上面一个方法也是需要通过调用BuildBindingMappings来建立映射。数据绑定始终是根据BindingMapping集合进行的。由于在BindingMapping中已经定义了完成数据绑定所需的必要信息,数据绑定的逻辑变得很简单。具体来说,数据绑定的逻辑是这样的:遍历所有的集合中每个BindingMapping,根据DataSourceProperty得到属性名称,然后进一步从数据源实体中得到具体的值。

  根据ControlValuePropertyType得到目标控件绑定属性的类型,然后将之前得到的值转换成该类型。最后,通过ControlValueProperty得到控件的绑定属性,将之前经过转换的值给控件的这个属性就可以了。整个数据绑定实现在如下一个OnBindData方法中。关于属性操作则借助于PropertyAccessor这个组件。

 
 
protected virtual void OnBindData(IEnumerable < BindingMapping > bindingMappings, object entity)
{
foreach (var mapping in bindingMappings)
{
var bindingMapping
= mapping.Clone();
object value = PropertyAccessor.Get(entity, bindingMapping.DataSourceProperty);
if ( null != this .DataItemBinding)
{
var args
= new DataBindingEventArgs(bindingMapping, value);
this .DataItemBinding( this , args);
value
= args.DataValue;
}
if ( ! bindingMapping.AutomaticBind)
{
continue ;
}

if ( ! string .IsNullOrEmpty(bindingMapping.FormatString))
{
value
= Format(value, bindingMapping.FormatString);
}

Type controlValuePropertyType
= PropertyAccessor.GetPropertyType(bindingMapping.Control.GetType(), bindingMapping.ControlValueProperty);
value
= ChangeType(value, controlValuePropertyType);
if ( null == value && typeof (ValueType).IsAssignableFrom(controlValuePropertyType))
{
value
= Activator.CreateInstance(controlValuePropertyType);
}
PropertyAccessor.Set(bindingMapping.Control, bindingMapping.ControlValueProperty, value);
if ( null != this .DataItemBound)
{
this .DataItemBound( this , new DataBindingEventArgs(bindingMapping, value));
}
}
}

  DataBinder设计的目标是让默认的绑定行为解决80%的问题,并且提供给相应的方式去解决余下的问题。为了让开发者能够有效解决余下的这20%的绑定问题,我们定义两个事件:DataItemBinding和DataBound,它们分别在进行绑定之前和之后被触发。关于事件的触发,已经体现在OnBindData方法的定义中了。

  五、通过映射集合实现数据捕捉

  数据绑定使用到的实际上是Entity-〉Control映射,如果我们借助控件到Control-〉Entity,就能实现自动捕获控件的值然后将其保存到给定的实体对象上。我为此在DataBinder上定义了两个重载的UpdateData方法。

 
 
public class DataBinder
{
// ...
public void BindData( object entity,IEnumerable < BindingMapping > bindingMappings);
public void UpdateData( object entity, Control container, string suffix = "" );
}

  UpdateData方法的实现和BindData方法的逻辑基本一致,将Control和Entity呼唤一下而已,所以在这里我就不再赘言叙述了。

目录
相关文章
|
3月前
|
API
【Azure 媒体服务】Media Service的编码示例 -- 创建缩略图子画面的.NET代码调试问题
【Azure 媒体服务】Media Service的编码示例 -- 创建缩略图子画面的.NET代码调试问题
|
3月前
|
C# 开发者 Windows
在VB.NET项目中使用C#编写的代码
在VB.NET项目中使用C#编写的代码
53 0
|
27天前
|
前端开发 JavaScript C#
CodeMaid:一款基于.NET开发的Visual Studio代码简化和整理实用插件
CodeMaid:一款基于.NET开发的Visual Studio代码简化和整理实用插件
|
3月前
|
Kubernetes 监控 Devops
【独家揭秘】.NET项目中的DevOps实践:从代码提交到生产部署,你不知道的那些事!
【8月更文挑战第28天】.NET 项目中的 DevOps 实践贯穿代码提交到生产部署全流程,涵盖健壮的源代码管理、GitFlow 工作流、持续集成与部署、容器化及监控日志记录。通过 Git、CI/CD 工具、Kubernetes 及日志框架的最佳实践应用,显著提升软件开发效率与质量。本文通过具体示例,助力开发者构建高效可靠的 DevOps 流程,确保项目成功交付。
73 0
|
3月前
|
XML 开发框架 .NET
.NET框架:软件开发领域的瑞士军刀,如何让初学者变身代码艺术家——从基础架构到独特优势,一篇不可错过的深度解读。
【8月更文挑战第28天】.NET框架是由微软推出的统一开发平台,支持多种编程语言,简化应用程序的开发与部署。其核心组件包括公共语言运行库(CLR)和类库(FCL)。CLR负责内存管理、线程管理和异常处理等任务,确保代码稳定运行;FCL则提供了丰富的类和接口,涵盖网络、数据访问、安全性等多个领域,提高开发效率。此外,.NET框架还支持跨语言互操作,允许开发者使用C#、VB.NET等语言编写代码并无缝集成。这一框架凭借其强大的功能和广泛的社区支持,已成为软件开发领域的重要工具,适合初学者深入学习以奠定职业生涯基础。
99 1
|
3月前
|
API
【Azure Key Vault】.NET 代码如何访问中国区的Key Vault中的机密信息(Get/Set Secret)
【Azure Key Vault】.NET 代码如何访问中国区的Key Vault中的机密信息(Get/Set Secret)
|
3月前
|
存储 Linux 网络安全
【Azure App Service】.NET代码实验App Service应用中获取TLS/SSL 证书 (App Service Linux/Linux Container)
【Azure App Service】.NET代码实验App Service应用中获取TLS/SSL 证书 (App Service Linux/Linux Container)
|
3月前
|
网络安全 API 数据安全/隐私保护
【Azure App Service】.NET代码实验App Service应用中获取TLS/SSL 证书 (App Service Windows)
【Azure App Service】.NET代码实验App Service应用中获取TLS/SSL 证书 (App Service Windows)
|
3月前
|
开发框架 .NET 编译器
【Azure Developer】使用Azure PubSub服务示例代码时候遇见了.NET 6.0的代码转换问题
【Azure Developer】使用Azure PubSub服务示例代码时候遇见了.NET 6.0的代码转换问题
|
3月前
分享一份 .NET Core 简单的自带日志系统配置,平时做一些测试或个人代码研究,用它就可以了
分享一份 .NET Core 简单的自带日志系统配置,平时做一些测试或个人代码研究,用它就可以了
下一篇
无影云桌面