一句代碼實(shí)現(xiàn)批量數(shù)據(jù)綁定 上
對(duì)于一個(gè)以數(shù)據(jù)處理為主的應(yīng)用中的UI層,我們往往需要編寫相當(dāng)多的代碼去實(shí)現(xiàn)數(shù)據(jù)綁定。如果界面上的控件和作為數(shù)據(jù)源的實(shí)體類型之間存儲(chǔ)某種約定的映射關(guān)系,我們就可以實(shí)現(xiàn)批量的數(shù)據(jù)綁定。為了驗(yàn)證這種想法,我寫了一個(gè)小小的組件。這個(gè)小玩意僅僅是我花了兩個(gè)小時(shí)寫的,其中還有很多問題沒有解決,比如對(duì)于空值的處理,特殊控件屬性值的HTML編碼問題,以及頻繁反射的性能問題,僅僅演示一種解決思路而已。本篇著重介紹如何通過這個(gè)組件來解決我們?cè)谶M(jìn)行數(shù)據(jù)綁定過程中的常見問題,下篇會(huì)介紹它的設(shè)計(jì)。
目錄:
- 基于控件ID/實(shí)體屬性名映射的數(shù)據(jù)綁定
- 一句代碼實(shí)現(xiàn)批量數(shù)據(jù)綁定
- 修正綁定數(shù)據(jù)的顯示格式
- 過濾不需要綁定的屬性
- 多個(gè)控件對(duì)應(yīng)同一個(gè)實(shí)體屬性
#p#
一、基于控件ID/實(shí)體屬性名映射的數(shù)據(jù)綁定
我的這個(gè)組件暫時(shí)命名為DataBinder好了(注意和System.Web.UI.DataBinder區(qū)分),我們用它來將一個(gè)實(shí)體對(duì)象綁定給指定的容器控件中的所有子控件。下面是DataBinder的定義,兩個(gè)BindData方法實(shí)現(xiàn)具體的綁定操作。
- public class DataBinder
- public event EventHandler<DataBindingEventArgs> DataItemBinding;
- public event EventHandler<DataBindingEventArgs> DataItemBound;
- public static IEnumerable<BindingMapping> BuildBindingMappings(Type entityType, Control container, string suffix = "");
- public void BindData(object entity, Control container, string suffix = "");
- public void BindData( object entity,IEnumerable<BindingMapping> bindingMappings);
- }
本文開頭所說,自動(dòng)批量的數(shù)據(jù)綁定依賴于控件和作為數(shù)據(jù)源實(shí)體類型的映射關(guān)系。在這里,我直接采用控件ID和實(shí)體屬性名之間的映射。也就是說,在對(duì)于界面上控件進(jìn)行命名的時(shí)候,應(yīng)該根據(jù)對(duì)應(yīng)的實(shí)體類型屬性名進(jìn)行規(guī)范命名。
另一方面,作為數(shù)據(jù)源的對(duì)象來說,它的所有屬性并不都是為數(shù)據(jù)綁定而涉及。為了讓DataBinder能夠自動(dòng)篩選用于綁定的屬性,我在相應(yīng)的屬性上應(yīng)用了一個(gè)自定義特性:DataPropertyAttribute。比如,下面的Customer對(duì)象會(huì)在后續(xù)的演示中用到,它的每一個(gè)數(shù)據(jù)屬性都應(yīng)用了這樣一個(gè)DataPropertyAttribute特性。
- public class Customer
- {
- [DataProperty]
- public string ID { get; set; }
- [DataProperty]
- public string FirstName { get; set; }
- [DataProperty]
- public string LastName { get; set; }
- [DataProperty]
- public string Gender { get; set; }
- [DataProperty]
- public int? Age { get; set; }
- [DataProperty]
- public DateTime? BirthDay { get; set; }
- [DataProperty]
- public bool? IsVip { get; set; }
- }
#p#
二、一句代碼實(shí)現(xiàn)批量數(shù)據(jù)綁定
現(xiàn)在我們就來演示如何通過我們定義的DataBinder實(shí)現(xiàn)“一句代碼的數(shù)據(jù)批量綁定”,而作為數(shù)據(jù)源就是我們上面定義的Customer對(duì)象。我們先來設(shè)計(jì)我們的頁面,下面是主體部分的HTML,這是一個(gè)表格。需要注意的是:所有需要綁定到Customer對(duì)象的空間都和對(duì)應(yīng)的屬性具有相同的ID。
- <table>
- <tr>
- <td style="width:20%;text-align:right">ID:</td>
- <td><asp:Label ID="ID" runat="server"></asp:Label></td>
- </tr>
- <tr>
- <td style="width:20%;text-align:right">First Name:</td>
- <td><asp:TextBox ID="FirstName" runat="server"></asp:TextBox></td>
- </tr>
- <tr>
- <td style="width:20%;text-align:right">Last Name:</td>
- <td><asp:TextBox ID="LastName" runat="server"></asp:TextBox></td>
- </tr>
- <tr>
- <td style="width:20%;text-align:right">Gender:</td>
- <td>
- <asp:RadioButtonList ID="Gender" runat="server" RepeatDirection="Horizontal">
- <asp:ListItem Text="Male" Value = "Male" />
- <asp:ListItem Text="Female" Value = "Female" />
- </asp:RadioButtonList>
- </td>
- </tr>
- <tr>
- <td style="width:20%;text-align:right">Age:</td>
- <td><asp:TextBox ID="Age" runat="server"></asp:TextBox></td>
- </tr>
- <tr>
- <td style="width:20%;text-align:right">Birthday:</td>
- <td><asp:TextBox ID="Birthday" runat="server" Width="313px"></asp:TextBox></td>
- </tr>
- <tr>
- <td style="width:20%;text-align:right">Is VIP:</td>
- <td><asp:CheckBox ID="IsVip" runat="server"></asp:CheckBox></td>
- </tr>
- <tr>
- <td colspan="2" align="center">
- <asp:Button ID="ButtonBind" runat="server" Text="Bind" onclick="ButtonBind_Click" />
- </td>
- </tr>
- /table>
為了編成方便,將DataBinder對(duì)象作為Page類型的一個(gè)屬性,該屬性在構(gòu)造函數(shù)中初始化。
- public partial class Default : System.Web.UI.Page
- {
- public Artech.DataBinding.DataBinder DataBinder { get; private set; }
- public Default()
- {
- this.DataBinder = new Artech.DataBinding.DataBinder();
- }
- }
然后我將數(shù)據(jù)綁定操作實(shí)現(xiàn)的Bind按照的Click事件中,對(duì)應(yīng)所有的代碼如下所示——真正的用于數(shù)據(jù)綁定的代碼只有一句。
- protected void ButtonBind_Click(object sender, EventArgs e)
- {
- var customer = new Customer
- {
- ID = Guid.NewGuid().ToString(),
- FirstName = "Zhang",
- LastName = "San",
- Age = 30,
- Gender = "Male",
- BirthDay = new DateTime(1981, 1, 1),
- IsVip = true
- };
- this.DataBinder.BindData(customer, this);
在瀏覽器中打開該Web頁面,點(diǎn)擊Bind按鈕,你會(huì)發(fā)現(xiàn)綁定的數(shù)據(jù)已經(jīng)正確顯示在了對(duì)應(yīng)的控件中:
#p#
三、修正綁定數(shù)據(jù)的顯示格式
雖然通過DataBinder實(shí)現(xiàn)了對(duì)多個(gè)控件的批量綁定,但是并不***。一個(gè)顯著的問題是:作為生日的字段不僅僅顯示了日期,還顯示了時(shí)間。我們?nèi)绾巫屓掌诎凑瘴覀円蟮母袷竭M(jìn)行顯示呢?DataBinder為了提供了三種選擇。
如果你注意看DataBinder定義了,你會(huì)發(fā)現(xiàn)它定義了兩個(gè)事件:DataItemBinding和DataItemBound(命名有待商榷),它們分別在對(duì)某個(gè)控件進(jìn)行綁定之前和之后觸發(fā)。我們的***種方案就是注冊(cè)DataItemBinding時(shí)間,為Birthday指定一個(gè)格式化字符串。假設(shè)我們需要的格式是“月-日-年”,那么我們指定的格式化字符串:MM-dd-yyyy。事件注冊(cè)我方在了Page的構(gòu)造函數(shù)中:
- public Default()
- {
- this.DataBinder = new Artech.DataBinding.DataBinder();
- this.DataBinder.DataItemBinding += (sender, args) =>
- {
- if (args.BindingMapping.Control == this.Birthday)
- {
- args.BindingMapping.FormatString = "MM-dd-yyyy";
- }
- };
- }
運(yùn)行程序,你會(huì)發(fā)現(xiàn)作為生日的字段已經(jīng)按照我們希望的格式顯示出來:
上面介紹了通過注冊(cè)DataItemBinding事件在綁定前指定格式化字符串的解決方案,你也可以通過注冊(cè)DataItemBound事件在綁定后修正顯示的日期格式,相應(yīng)的代碼如下:
- public Default()
- {
- this.DataBinder = new Artech.DataBinding.DataBinder();
- this.DataBinder.DataItemBound += (sender, args) =>
- {
- if (args.BindingMapping.Control == this.Birthday && null != args.DataValue)
- {
- this.Birthday.Text = ((DateTime)Convert.ChangeType(args.DataValue, typeof(DateTime))).
- ToString("MM-dd-yyyy");
- }
- };
DataBinder定義了兩個(gè)BindData重載,我們使用的是通過指定數(shù)據(jù)源和容器控件的方式,而另一個(gè)重載的參數(shù)為IEnumerable<BindingMapping>類型。而BindingMapping是我們自定義的類型,用于表示控件和實(shí)體屬性之間的運(yùn)行時(shí)映射關(guān)系。而這樣一個(gè)BindingMapping集合,可以通過DataBinder的靜態(tài)方法BuildBindingMappings來創(chuàng)建。BindingMapping具有一個(gè)FormatString表示格式化字符串(實(shí)際上面我們指定的格式化字符串就是為這個(gè)屬性指定的)。那么,我們也可以通過下面的代碼來進(jìn)行數(shù)據(jù)綁定:
- protected void ButtonBind_Click(object sender, EventArgs e)
- {
- var customer = new Customer
- {
- ID = Guid.NewGuid().ToString(),
- FirstName = "Zhang",
- LastName = "San",
- Age = 30,
- Gender = "Male",
- BirthDay = new DateTime(1981, 1, 1),
- IsVip = true
- };
- var bindingMappings = Artech.DataBinding.DataBinder.BuildBindingMappings(typeof(Customer), this);
- bindingMappings.Where(mapping => mapping.Control == this.Birthday).First().FormatString = "MM-dd-yyyy";
- this.DataBinder.BindData(customer, bindingMappings);
#p#
四、過濾不需要綁定的屬性
在默認(rèn)的情況下,***個(gè)BindData方法(指定容器控件)會(huì)遍歷實(shí)體的所有屬性,將其綁定到對(duì)應(yīng)的控件上??赡茉谟械臅r(shí)候,對(duì)于某些特殊的屬性,我們不需要進(jìn)行綁定。比如,某個(gè)控件的ID雖然符合實(shí)體屬性的映射,但是它們表示的其實(shí)根本不是相同性質(zhì)的數(shù)據(jù)。
為了解決在這個(gè)問題,在BindingMapping類型中定義了一個(gè)布爾類型的AutomaticBind屬性。如果你在綁定前將該屬性設(shè)置成False,那么基于該BindingMapping的數(shù)據(jù)綁定將被忽略。如果你調(diào)用BindData(object entity, Control container, string suffix = "")這個(gè)重載,你可以通過注冊(cè)DataItemBinding事件將相應(yīng)BindingMapping的AutomaticBind屬性設(shè)置成False。如果你調(diào)用BindData( object entity,IEnumerable<BindingMapping> bindingMappings)這個(gè)重載,你只需要在調(diào)用之間將相應(yīng)BindingMapping的AutomaticBind屬性設(shè)置成False。
我們將我們的程序還原成最初的狀態(tài),現(xiàn)在通過注冊(cè)BindingMapping事件將基于Birthday的BindingMapping的AutomaticBind屬性設(shè)置成False:
- public Default()
- {
- this.DataBinder = new Artech.DataBinding.DataBinder();
- this.DataBinder.DataItemBinding += (sender, args) =>
- {
- if (args.BindingMapping.Control == this.Birthday)
- {
- args.BindingMapping.AutomaticBind = false;
- }
- };
- }
程序執(zhí)行后,Birthday對(duì)應(yīng)的TextBox將不會(huì)被綁定:
#p#
五、多個(gè)控件對(duì)應(yīng)同一個(gè)實(shí)體屬性
在上面的例子中,我們的控件的ID和對(duì)應(yīng)的實(shí)體屬性是相同的。但是在很多情況下,相同的頁面上有不止一個(gè)控件映射到實(shí)體的同一個(gè)屬性上。而控件ID的唯一性決定了我們不能為它們起相同的ID。在這種情況下,我們采用“基于后綴”的映射。也就是為,在為控件進(jìn)行命名的時(shí)候,通過“實(shí)體屬性名+后綴”形式來指定。
如果你仔細(xì)看了DataBinder的定義,不論是實(shí)例方法BindData(接受Control類型參數(shù)的),還是靜態(tài)方法BuildBindingMappings,都具有一個(gè)缺省參數(shù)suffix,這就是為這種情況設(shè)計(jì)的。在默認(rèn)的情況下,這個(gè)參數(shù)的值為空字符串,所以我們需要控件和實(shí)體屬性具有相同的名稱。如果控件是基于“實(shí)體屬性名+后綴”來命名的,就需要顯式指定這個(gè)參數(shù)了。為了演示這種情況,我們將例子中的所有需要綁定的空間ID加上一個(gè)“_Xyz”字符作為后綴。
- <table>
- <tr>
- <td style="width:20%;text-align:right">ID:</td>
- <td><asp:Label ID="ID_Xyz" runat="server"></asp:Label></td>
- </tr>
- <tr>
- <td style="width:20%;text-align:right">First Name:</td>
- <td><asp:TextBox ID="FirstName_Xyz" runat="server"></asp:TextBox></td>
- </tr>
- <tr>
- <td style="width:20%;text-align:right">Last Name:</td>
- <td><asp:TextBox ID="LastName_Xyz" runat="server"></asp:TextBox></td>
- </tr>
- <tr>
- <td style="width:20%;text-align:right">Gender:</td>
- <td>
- <asp:RadioButtonList ID="Gender_Xyz" runat="server" RepeatDirection="Horizontal">
- <asp:ListItem Text="Male" Value = "Male" />
- <asp:ListItem Text="Female" Value = "Female" />
- </asp:RadioButtonList>
- </td>
- </tr>
- <tr>
- <td style="width:20%;text-align:right">Age:</td>
- <td><asp:TextBox ID="Age_Xyz" runat="server"></asp:TextBox></td>
- </tr>
- <tr>
- <td style="width:20%;text-align:right">Birthday:</td>
- <td><asp:TextBox ID="Birthday_Xyz" runat="server" Width="313px"></asp:TextBox></td>
- </tr>
- <tr>
- <td style="width:20%;text-align:right">Is VIP:</td>
- <td><asp:CheckBox ID="IsVip_Xyz" runat="server"></asp:CheckBox></td>
- </tr>
- <tr>
- <td colspan="2" align="center">
- <asp:Button ID="ButtonBind" runat="server" Text="Bind" onclick="ButtonBind_Click" />
- </td>
- </tr>
- /table>
如果采用指定容器控件進(jìn)行直接綁定的話,就可以這樣編程:
- protected void ButtonBind_Click(object sender, EventArgs e)
- {
- var customer = new Customer
- {
- ID = Guid.NewGuid().ToString(),
- FirstName = "Zhang",
- LastName = "San",
- Age = 30,
- Gender = "Male",
- BirthDay = new DateTime(1981, 1, 1),
- IsVip = true
- };
- this.DataBinder.BindData(customer, this, "_Xyz");
如果通過預(yù)先創(chuàng)建的BindingMapping集合進(jìn)行數(shù)據(jù)綁定,那么代碼將是這樣:
- protected void ButtonBind_Click(object sender, EventArgs e)
- {
- var customer = new Customer
- {
- ID = Guid.NewGuid().ToString(),
- FirstName = "Zhang",
- LastName = "San",
- Age = 30,
- Gender = "Male",
- BirthDay = new DateTime(1981, 1, 1),
- IsVip = true
- };
- var bindingMappings = Artech.DataBinding.DataBinder.BuildBindingMappings(typeof(Customer), this, "_Xyz");
- this.DataBinder.BindData(customer, bindingMappings);
- }
原文鏈接:http://www.cnblogs.com/artech/archive/2011/03/23/databinding.html
【編輯推薦】
- DBA應(yīng)用技巧:如何升級(jí)InnoDB Plugin
- 十個(gè)節(jié)省時(shí)間的MySQL命令
- DBA必備:MySQL數(shù)據(jù)庫常用操作和技巧
- MySQL日志操作教程:DBA們管理的利器
- MySQL觸發(fā)器如何正確使用