一句代码实现批量数据绑定 上
创始人
2024-07-24 02:31:28
0

对于一个以数据处理为主的应用中的UI层,我们往往需要编写相当多的代码去实现数据绑定。如果界面上的控件和作为数据源的实体类型之间存储某种约定的映射关系,我们就可以实现批量的数据绑定。为了验证这种想法,我写了一个小小的组件。这个小玩意仅仅是我花了两个小时写的,其中还有很多问题没有解决,比如对于空值的处理,特殊控件属性值的HTML编码问题,以及频繁反射的性能问题,仅仅演示一种解决思路而已。本篇着重介绍如何通过这个组件来解决我们在进行数据绑定过程中的常见问题,下篇会介绍它的设计。

目录:

  1. 基于控件ID/实体属性名映射的数据绑定
  2. 一句代码实现批量数据绑定
  3. 修正绑定数据的显示格式
  4. 过滤不需要绑定的属性
  5. 多个控件对应同一个实体属性

#p#

一、基于控件ID/实体属性名映射的数据绑定

我的这个组件暂时命名为DataBinder好了(注意和System.Web.UI.DataBinder区分),我们用它来将一个实体对象绑定给指定的容器控件中的所有子控件。下面是DataBinder的定义,两个BindData方法实现具体的绑定操作。

  1. public class DataBinder     
  2.      
  3.     public event EventHandler DataItemBinding;     
  4.     public event EventHandler DataItemBound;     
  5.       
  6.     public static IEnumerable BuildBindingMappings(Type entityType, Control container, string suffix = "");             
  7.     public void BindData(object entity, Control container, string suffix = "");     
  8.     public void BindData( object entity,IEnumerable bindingMappings);    

本文开头所说,自动批量的数据绑定依赖于控件和作为数据源实体类型的映射关系。在这里,我直接采用控件ID和实体属性名之间的映射。也就是说,在对于界面上控件进行命名的时候,应该根据对应的实体类型属性名进行规范命名。

另一方面,作为数据源的对象来说,它的所有属性并不都是为数据绑定而涉及。为了让DataBinder能够自动筛选用于绑定的属性,我在相应的属性上应用了一个自定义特性:DataPropertyAttribute。比如,下面的Customer对象会在后续的演示中用到,它的每一个数据属性都应用了这样一个DataPropertyAttribute特性。

  1. public class Customer     
  2. {     
  3.     [DataProperty]     
  4.     public string ID { get; set; }     
  5.     [DataProperty]     
  6.     public string FirstName { get; set; }     
  7.     [DataProperty]     
  8.     public string LastName { get; set; }     
  9.     [DataProperty]    
  10.     public string Gender { get; set; }    
  11.     [DataProperty]    
  12.     public int? Age { get; set; }    
  13.     [DataProperty]    
  14.     public DateTime? BirthDay { get; set; }    
  15.     [DataProperty]    
  16.     public bool? IsVip { get; set; }    

#p#

二、一句代码实现批量数据绑定

现在我们就来演示如何通过我们定义的DataBinder实现“一句代码的数据批量绑定”,而作为数据源就是我们上面定义的Customer对象。我们先来设计我们的页面,下面是主体部分的HTML,这是一个表格。需要注意的是:所有需要绑定到Customer对象的空间都和对应的属性具有相同的ID。

  1.      
  2.  
  3.      
  4.      ID:     
  5.      
  6.      
  7.  
  8.      
  9.   
  10.      
  11.      First Name:     
  12.      
  13.      
  14.  
  15.     
  16.   
  17.     
  18.      Last Name:    
  19.      
  20.     
  21.  
  22.     
  23.   
  24.     
  25.      Gender:    
  26.      
  27.     
  28.  
  29.     
  30.   
  31.     
  32.      Age:    
  33.      
  34.     
  35.  
  36.     
  37.   
  38.     
  39.      Birthday:    
  40.      
  41.     
  42.  
  43.     
  44.   
  45.     
  46.      Is VIP:    
  47.      
  48.     
  49.  
  50.     
  51.   
  52.      
  53.          
  54.              
  55.          
  56.  
  57.     
  58. /table> 
  59. 为了编成方便,将DataBinder对象作为Page类型的一个属性,该属性在构造函数中初始化。

    1. public partial class Default : System.Web.UI.Page  
    2. {  
    3.     public Artech.DataBinding.DataBinder DataBinder { get; private set; }  
    4.     public Default()  
    5.     {  
    6.         this.DataBinder = new Artech.DataBinding.DataBinder();  
    7.     }  

    然后我将数据绑定操作实现的Bind按照的Click事件中,对应所有的代码如下所示——真正的用于数据绑定的代码只有一句。

    1. protected void ButtonBind_Click(object sender, EventArgs e)  
    2. {  
    3.     var customer = new Customer  
    4.     {  
    5.         ID          = Guid.NewGuid().ToString(),  
    6.         FirstName   = "Zhang",  
    7.         LastName    = "San",  
    8.         Age         = 30,  
    9.         Gender      = "Male",  
    10.        BirthDay    = new DateTime(1981, 1, 1),  
    11.        IsVip       = true 
    12.    };  
    13.    this.DataBinder.BindData(customer, this);  

    在浏览器中打开该Web页面,点击Bind按钮,你会发现绑定的数据已经正确显示在了对应的控件中:

     

    #p#

    三、修正绑定数据的显示格式

    虽然通过DataBinder实现了对多个控件的批量绑定,但是并不***。一个显著的问题是:作为生日的字段不仅仅显示了日期,还显示了时间。我们如何让日期按照我们要求的格式进行显示呢?DataBinder为了提供了三种选择。

    如果你注意看DataBinder定义了,你会发现它定义了两个事件:DataItemBinding和DataItemBound(命名有待商榷),它们分别在对某个控件进行绑定之前和之后触发。我们的***种方案就是注册DataItemBinding时间,为Birthday指定一个格式化字符串。假设我们需要的格式是“月-日-年”,那么我们指定的格式化字符串:MM-dd-yyyy。事件注册我方在了Page的构造函数中:

    1.  public Default()  
    2.  {  
    3.      this.DataBinder = new Artech.DataBinding.DataBinder();  
    4.      this.DataBinder.DataItemBinding += (sender, args) => 
    5.          {  
    6.              if (args.BindingMapping.Control == this.Birthday)  
    7.              {  
    8.                  args.BindingMapping.FormatString = "MM-dd-yyyy";  
    9.              }  
    10.         };  

    运行程序,你会发现作为生日的字段已经按照我们希望的格式显示出来:

     

    上面介绍了通过注册DataItemBinding事件在绑定前指定格式化字符串的解决方案,你也可以通过注册DataItemBound事件在绑定后修正显示的日期格式,相应的代码如下:

    1. public Default()  
    2. {  
    3.     this.DataBinder = new Artech.DataBinding.DataBinder();  
    4.     this.DataBinder.DataItemBound += (sender, args) => 
    5.         {  
    6.             if (args.BindingMapping.Control == this.Birthday && null != args.DataValue)  
    7.             {  
    8.                 this.Birthday.Text = ((DateTime)Convert.ChangeType(args.DataValue, typeof(DateTime))).  
    9.                     ToString("MM-dd-yyyy");  
    10.            }  
    11.        };  

    DataBinder定义了两个BindData重载,我们使用的是通过指定数据源和容器控件的方式,而另一个重载的参数为IEnumerable类型。而BindingMapping是我们自定义的类型,用于表示控件和实体属性之间的运行时映射关系。而这样一个BindingMapping集合,可以通过DataBinder的静态方法BuildBindingMappings来创建。BindingMapping具有一个FormatString表示格式化字符串(实际上面我们指定的格式化字符串就是为这个属性指定的)。那么,我们也可以通过下面的代码来进行数据绑定:

    1. protected void ButtonBind_Click(object sender, EventArgs e)  
    2. {  
    3.     var customer = new Customer  
    4.     {  
    5.         ID          = Guid.NewGuid().ToString(),  
    6.         FirstName   = "Zhang",  
    7.         LastName    = "San",  
    8.         Age         = 30,  
    9.         Gender      = "Male",  
    10.        BirthDay    = new DateTime(1981, 1, 1),  
    11.        IsVip       = true 
    12.    };  
    13.    var bindingMappings = Artech.DataBinding.DataBinder.BuildBindingMappings(typeof(Customer), this);  
    14.    bindingMappings.Where(mapping => mapping.Control == this.Birthday).First().FormatString = "MM-dd-yyyy";  
    15.    this.DataBinder.BindData(customer, bindingMappings);  

    #p#

    四、过滤不需要绑定的属性

    在默认的情况下,***个BindData方法(指定容器控件)会遍历实体的所有属性,将其绑定到对应的控件上。可能在有的时候,对于某些特殊的属性,我们不需要进行绑定。比如,某个控件的ID虽然符合实体属性的映射,但是它们表示的其实根本不是相同性质的数据。

    为了解决在这个问题,在BindingMapping类型中定义了一个布尔类型的AutomaticBind属性。如果你在绑定前将该属性设置成False,那么基于该BindingMapping的数据绑定将被忽略。如果你调用BindData(object entity, Control container, string suffix = "")这个重载,你可以通过注册DataItemBinding事件将相应BindingMapping的AutomaticBind属性设置成False。如果你调用BindData( object entity,IEnumerable bindingMappings)这个重载,你只需要在调用之间将相应BindingMapping的AutomaticBind属性设置成False。

    我们将我们的程序还原成最初的状态,现在通过注册BindingMapping事件将基于Birthday的BindingMapping的AutomaticBind属性设置成False:

    1.  public Default()  
    2.  {  
    3.      this.DataBinder = new Artech.DataBinding.DataBinder();  
    4.      this.DataBinder.DataItemBinding += (sender, args) => 
    5.          {  
    6.              if (args.BindingMapping.Control == this.Birthday)  
    7.              {  
    8.                  args.BindingMapping.AutomaticBind = false;  
    9.              }  
    10.         };  

    程序执行后,Birthday对应的TextBox将不会被绑定:

     

    #p#

    五、多个控件对应同一个实体属性

    在上面的例子中,我们的控件的ID和对应的实体属性是相同的。但是在很多情况下,相同的页面上有不止一个控件映射到实体的同一个属性上。而控件ID的唯一性决定了我们不能为它们起相同的ID。在这种情况下,我们采用“基于后缀”的映射。也就是为,在为控件进行命名的时候,通过“实体属性名+后缀”形式来指定。

    如果你仔细看了DataBinder的定义,不论是实例方法BindData(接受Control类型参数的),还是静态方法BuildBindingMappings,都具有一个缺省参数suffix,这就是为这种情况设计的。在默认的情况下,这个参数的值为空字符串,所以我们需要控件和实体属性具有相同的名称。如果控件是基于“实体属性名+后缀”来命名的,就需要显式指定这个参数了。为了演示这种情况,我们将例子中的所有需要绑定的空间ID加上一个“_Xyz”字符作为后缀。

        
  60.              
  61.                  
  62.                  
  63.              
  64.      
  65.  
  66.  
  67.  
  68.      ID: 
  69.      
  70.  
  71.  
  72.  
  73.   
  74.  
  75.      First Name: 
  76.      
  77.  
  78.  
  79.  
  80.  
  81.  
  82.     Last Name: 
  83.     
  84.  
  85.  
  86.  
  87.  
  88.     Gender: 
  89.     
  90.  
  91.  
  92.  
  93.     Age: 
  94.     
  95.  
  96.  
  97.  
  98.  
  99.     Birthday: 
  100.     
  101.  
  102.  
  103.  
  104.  
  105.     Is VIP: 
  106.     
  107.  
  108.  
  109.  
  110.  
  111.      
  112.          
  113.      
  114.  
  115. /table> 
  116. 如果采用指定容器控件进行直接绑定的话,就可以这样编程:

    1. protected void ButtonBind_Click(object sender, EventArgs e)  
    2. {  
    3.     var customer = new Customer  
    4.     {  
    5.         ID          = Guid.NewGuid().ToString(),  
    6.         FirstName   = "Zhang",  
    7.         LastName    = "San",  
    8.         Age         = 30,  
    9.         Gender      = "Male",  
    10.        BirthDay    = new DateTime(1981, 1, 1),  
    11.        IsVip       = true 
    12.    };  
    13.    this.DataBinder.BindData(customer, this, "_Xyz");  

    如果通过预先创建的BindingMapping集合进行数据绑定,那么代码将是这样:

    1.  protected void ButtonBind_Click(object sender, EventArgs e)  
    2.  {  
    3.      var customer = new Customer  
    4.      {  
    5.          ID          = Guid.NewGuid().ToString(),  
    6.          FirstName   = "Zhang",  
    7.          LastName    = "San",  
    8.          Age         = 30,  
    9.          Gender      = "Male",  
    10.         BirthDay    = new DateTime(1981, 1, 1),  
    11.         IsVip       = true 
    12.     };  
    13.     
    14.     var bindingMappings = Artech.DataBinding.DataBinder.BuildBindingMappings(typeof(Customer), this, "_Xyz");  
    15.     this.DataBinder.BindData(customer, bindingMappings);  

     原文链接:http://www.cnblogs.com/artech/archive/2011/03/23/databinding.html

    【编辑推荐】

    1. DBA应用技巧:如何升级InnoDB Plugin
    2. 十个节省时间的MySQL命令
    3. DBA必备:MySQL数据库常用操作和技巧
    4. MySQL日志操作教程:DBA们管理的利器
    5. MySQL触发器如何正确使用

     

    相关内容

    热门资讯

    如何允许远程连接到MySQL数... [[277004]]【51CTO.com快译】默认情况下,MySQL服务器仅侦听来自localhos...
    如何利用交换机和端口设置来管理... 在网络管理中,总是有些人让管理员头疼。下面我们就将介绍一下一个网管员利用交换机以及端口设置等来进行D...
    施耐德电气数据中心整体解决方案... 近日,全球能效管理专家施耐德电气正式启动大型体验活动“能效中国行——2012卡车巡展”,作为该活动的...
    Windows恶意软件20年“... 在Windows的早期年代,病毒游走于系统之间,偶尔删除文件(但被删除的文件几乎都是可恢复的),并弹...
    20个非常棒的扁平设计免费资源 Apple设备的平面图标PSD免费平板UI 平板UI套件24平图标Freen平板UI套件PSD径向平...
    德国电信门户网站可实时显示全球... 德国电信周三推出一个门户网站,直观地实时提供其安装在全球各地的传感器网络检测到的网络攻击状况。该网站...
    为啥国人偏爱 Mybatis,... 关于 SQL 和 ORM 的争论,永远都不会终止,我也一直在思考这个问题。昨天又跟群里的小伙伴进行...
    《非诚勿扰》红人闫凤娇被曝厕所... 【51CTO.com 综合消息360安全专家提醒说,“闫凤娇”、“非诚勿扰”已经被黑客盯上成为了“木...
     
  117.          
  118.              
  119.              
  120.          
  121.