数据绑定表达式(下):.NET发现之旅(二)
生活随笔
收集整理的這篇文章主要介紹了
数据绑定表达式(下):.NET发现之旅(二)
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
這一節繼續來談.NET中的數據綁定表達式。
本節涉及的內容如下:
一,數據綁定方法的來源以及在低層上的實現。
二,數據綁定方法的執行效率排序。 <%#Container.DataItem%>
<%#GetDataItem()%>
<%#Eval("字段名")%>
<%#DataBinder.Eval(Container.DataItem,"字段名")%>
<%#((DataRowView)Container.DataItem)["字段名"]?%>
<%#((Type)Container.DataItem).成員?%>
<%#((Type)GetDataItem()).成員?%>
上面七種綁定形式以及它們的變幻形式都用過嗎?性能怎么排序?
復習一下:第一節我們主要談了數據綁定表達式的各種形式,在ASP.NET頁面中出現的位置,以及我們常綁定到與數據庫有關的DataView,DataTable,DataSet?等數據源的數據綁定表達式的各種形式。
你有沒有對Eval方法和DataBinder.Eval方法好奇過?
在.NET2.0中我們經常用Eval方法在Repeater,DataList,GridView等循環控件中綁定數據,Eval方法和DataBinder.Eval方法在低層是怎么實現的?它們到底有什么千絲萬縷的關系?
一,來源、實現。
我們常用的Eval方法其實是Page類的一個靜態單向只讀方法,而且它是一個受保護的方法。實際上Page類的Eval方法是繼承自TemplateControl類的。TemplateControl?類是一個抽象類,它為Page?類和?UserControl?類提供通用屬性和方法。我們先來看一下繼承家譜:
System.Object?
?? System.Web.UI.Control?
????System.Web.UI.TemplateControl
?????? System.Web.UI.Page?
?????? System.Web.UI.UserControl?
Eval方法就是TemplateControl類的方法,它有兩種形式:
事實上TemplateControl類還提供了XPath方法和XPathSelect方法供Page類和UserControl繼承。這2個方法是和XML數據源有關的綁定方法。
如果細心的你查看TemplateControl類的基類Control類,你就會發現其實Control類并沒有提供Eval,XPath,XPathSelect等方法。所以Eval,XPath等方法最終是在TemplateControl類中實現的。
現在,終于找到了Eval,XPath等數據綁定方法的來源了。
Eval,XPath等方法是.NET 2.0新增的方法。在.NET 1.1時代我們經常用的是DateBinder.Eval方法。形如: <%#DataBind.Eval(Container.DataItem,"字段名")?%>
<%#DataBind.Eval(Container.DataItem,"字段名","{0:c}")?%> Eval的出現其實就是為了簡化DataBinder.Eval方法的寫法從而代替它。
在ASP.NET 2.0中及以上,當我們調用Eval時,Eval?方法會使用GetDataItem方法調用DataBinder.Eval方法計算表達式的值。要想理解這句話,就算查邊MSDN也一頭霧水,除非我們知道Eval方法的源代碼,否則根本找不到蛛絲馬跡。這里就要用到反射了。我們通過反射獲得了Eval方法的源代碼: protected???internal???object???Eval(string???expression)???
??{???
????????????this.CheckPageExists();???
????????????return???DataBinder.Eval(this.Page.GetDataItem(),???expression);???
??}??? 終于見到GetDataItem()方法了,其實它就是Page類的一個方法,也是.NET 2.0新增一個方法。GetDataItem()方法的作用是為了獲得Container.DataItem,它是.NET 2.0中用來代替Container.DataItem的,如果你曾經用Repeater和DataList等綁定過數組或者ArrayList等,你就會發現<%#GetDataItem()%>和<%#Container.DataItem%>等價。同時,可以肯定:Eval方法在低層上確實是調用DataBinder.Eval方法實現數據綁定的。其中“this.CheckPageExists();”?是檢查調用的時候有沒有Page對象的,如果沒有則會拋出一個異常。?
要弄清Eval是怎么工作的,GetDataItem()方法的低層實現我們也要用反射來獲取: public?object?GetDataItem()
????{
????????if?((this._dataBindingContext?==?null)?||?(this._dataBindingContext.Count?==?0))
????????{
????????????throw?new?InvalidOperationException(SR.GetString("Page_MissingDataBindingContext"));
????????}
????????return?this._dataBindingContext.Peek();
????}??? 我們從GatDataItem()方法中看到“return ? this._dataBindingContext.Peek();”很快就猜想_dataBindingContext是不是一個堆棧呢?事實它就是一個堆棧!通過反射查看源代碼我們得出:_dataBindingContext是一個Stack類型對象。所以它有Peek方法。“return ? this._dataBindingContext.Peek();?”正是把堆棧頂部的元素返回。而if語句是用來判斷這個堆棧是否已經存在或者是否已經有元素存在,如果if不成立,就會拋出一個異常。
從上面的分析我們知道:_dataBindingContext堆棧的作用是通過GetDataItem()方法這個橋梁向Eval方法提供Container.DateItem。用逆向思維來理解上面這句話:Eval方法可以自動計算出Container.DataItem,原因就是從dataBindingContext堆棧來獲取Container.DataItem;這也就為什么Eval方法能夠知道形如<%#Eval"字段名"%>中字段名隸屬于哪個數據項的屬性的原因;同時我們也知道.NET 2.0中的Eval在本質上的實現并沒有拋棄Container.DataItem,而Container.DataItem在2.0時代也沒有消失。
那么_dataBindingContext這個保存Container.DataItem的堆棧是怎么建立的呢?
我們很快就想到每次綁定控件時候最后那條語句是什么:this.控件ID.DataBind();對就是DataBind()方法,DataBind()方法還有一個重載:DataBind(bool raiseOnDataBinding)。為_dataBindingContext這個堆棧壓入元素和彈出元素的方法正是用DataBind(bool flag)這個重載方法實現的。
DataBind(bool raiseOnDataBinding)在低層的實現: ?protected?virtual?void?DataBind(bool?raiseOnDataBinding)
????{
????????bool?flag1?=?false;//這個標志的用處在上下文中很容易推出來,如果有DataItem壓棧,則在后面出棧。???
????????if?(this.IsBindingContainer)//判斷控件是不是數據綁定容器,實際上就是判斷控件類是不是實現了INamingContainer???
????????{
????????????bool?flag2;
????????????object?obj1?=?DataBinder.GetDataItem(this,?out???flag2);//這個方法是判斷控件是不是有DataItem屬性,并把它取出來。???
????????????if?(flag2?&&?(this.Page?!=?null))//如果控件有DataItem???
????????????{
????????????????this.Page.PushDataBindingContext(obj1);//把DataItem壓棧,PushDataBindingContext就是調用_dataBindingContext的Push方法???
????????????????flag1?=?true;
????????????}
????????}
????????try
????????{
????????????if?(raiseOnDataBinding)//這里是判斷是不是觸發DataBinding事件的。???
????????????{
????????????????this.OnDataBinding(EventArgs.Empty);
????????????}
????????????this.DataBindChildren();//對子控件進行數據綁定,如果這個控件有DataItem,則上面會將DataItem壓入棧頂,這樣,在子控件里面調用Eval或者GetDataItem方法,就會把剛剛壓進去的DataItem給取出來。???
????????}
????????finally
????????{
????????????if?(flag1)//如果剛才有壓棧,則現在彈出來。???
????????????{
????????????????this.Page.PopDataBindingContext();//PopDataBindingContext就是調用_dataBindingContext的Pop方法???
????????????}
????????}
????}???
當我們執行到this.控件ID.DataBind();時候。在低層上就會調用這個重載的方法來準備包含DataItem的_DatBindingContext堆棧。
上面的代碼中提到了DataBinding事件,那么它一般什么時候被觸發呢?
1,如果用編程方式,那么在我們調用DataBind()方法時候自動觸發DataBinding事件。
2,如果我們用數據源控件(例如SqlDataSource等),當把控件綁定到數據源控件時候,這個事件就會自動觸發。
一般數據綁定表達式常常放在模板中循環顯示數據,例如Repeater和DataList等的模板。那么下面這個知識點應該知道:Repeater,DataList,FormView等控件必須使用模板,如果不使用模板,這些控件將無法顯示數據。而GridView,DetailsView,Menu等控件也支持模板,但顯示數據時不是必須的。而TreeView控件不支持模板。
注意:一般情況下,數據綁定表達式不會自動計算它的值,除非它所在的頁或者控件顯示調用DataBind()方法。DataBind()方法能夠將數據源綁定到被調用的服務器控件及其所有子控件,同時分析并計算數據綁定表達式的值。
終于寫的有點眉目了,好累!我們該回頭看看Eval方法調用的靜態DataBinder.Eval方法在低層的實現了。我把DataBinder類的源代碼作為附近提供下載。 二,執行效率
從“一”講述的低層實現。我們很容易來排序下面數據綁定表達式的執行效率 <%#Container.DataItem%>
<%#GetDataItem()%>
<%#Eval("字段名")%>
<%#DataBinder.Eval(Container.DataItem,"字段名")%>
<%#((DataRowView)Container.DataItem)["字段名"]?%>
<%#((Type)Container.DataItem).成員?%>
<%#((Type)GetDataItem()).成員?%> 1,效率最高應該是: <%#((Type)Container.DataItem).成員?%>
<%#Container.DataItem%>
<%#((DataRowView)Container.DataItem)["字段名"]?%>
2,效率排第二的是: <%#((Type)GetDataItem()).成員?%>
<%#GetDataItem()%>
3,效率最低的是: <%#Eval("字段名")%>
<%#DataBinder.Eval(Container.DataItem,"字段名")%> 其實按上面的排序有失公允,原因是這七種數據表達綁定形式運用的場合不是完全相同的。
使用場合大概如下:
1,
<%#Eval("字段名")%>
<%#DataBinder.Eval(Container.DataItem,"字段名")%>
它們的使用場合最廣,數據源可以為與數據庫有關的DataSet,DataTable,DataView。也可以為普通集合(例如:數組,ArrayList,HashTable等)和泛行集合(例如:List<T>,Dictionary<Tkey,Tvalue>等)。
注:它們2個永遠可以相互替換,至少目前是這樣,凡是可以用Eval方法的地方,就可以用DataBinder.Eval方法替換。從低層實現上,Eval比DataBinder.Eval方法效率稍低,原因是Eval方法對了調用GetDataItem()方法這一步。但最終都是通過DataBinder.Eval方法利用反射技術根據名稱查找屬性,從而計算出表達式的值,所以非常影響性能。
2,
<%#((DataRowView)Container.DataItem)["字段名"]?%>
它只能使用在數據源為與數據庫有關的Dataset,DatTable,DataView。這些數據源都實現了IListSource接口。其實從低層實現本質上來看,它和<%#((Type)Container.DataItem).成員?%>類似。
3,
<%#Container.DataItem%>
<%#GetDataItem()%>
<%#((Type)Container.DataItem).成員?%>
<%#((Type)GetDataItem()).成員?%>
這幾種形式估計大家最不常用。它們一般只使用與普通集合(例如:數組,ArrayList,HashTable)和泛行集合(例如:List<T>,Dictionary<Tkey,Tvalue>)。其實本質上就是實現了IList,ICollection,IEnumerable,IDictionary等以及這些接口對應的泛行接口的集合。IList接口和IDictionary接口的區別是,一個只有值,而另一個是鍵/值對,對應泛行形式也是這樣。而Array就對用List<T>,而HashTable就對應Dictionary<Tkey,Tvalue>。
本節涉及的內容如下:
一,數據綁定方法的來源以及在低層上的實現。
二,數據綁定方法的執行效率排序。 <%#Container.DataItem%>
<%#GetDataItem()%>
<%#Eval("字段名")%>
<%#DataBinder.Eval(Container.DataItem,"字段名")%>
<%#((DataRowView)Container.DataItem)["字段名"]?%>
<%#((Type)Container.DataItem).成員?%>
<%#((Type)GetDataItem()).成員?%>
上面七種綁定形式以及它們的變幻形式都用過嗎?性能怎么排序?
復習一下:第一節我們主要談了數據綁定表達式的各種形式,在ASP.NET頁面中出現的位置,以及我們常綁定到與數據庫有關的DataView,DataTable,DataSet?等數據源的數據綁定表達式的各種形式。
你有沒有對Eval方法和DataBinder.Eval方法好奇過?
在.NET2.0中我們經常用Eval方法在Repeater,DataList,GridView等循環控件中綁定數據,Eval方法和DataBinder.Eval方法在低層是怎么實現的?它們到底有什么千絲萬縷的關系?
一,來源、實現。
我們常用的Eval方法其實是Page類的一個靜態單向只讀方法,而且它是一個受保護的方法。實際上Page類的Eval方法是繼承自TemplateControl類的。TemplateControl?類是一個抽象類,它為Page?類和?UserControl?類提供通用屬性和方法。我們先來看一下繼承家譜:
System.Object?
?? System.Web.UI.Control?
????System.Web.UI.TemplateControl
?????? System.Web.UI.Page?
?????? System.Web.UI.UserControl?
Eval方法就是TemplateControl類的方法,它有兩種形式:
| 名稱 | 說明 |
| TemplateControl.Eval (String) | 計算數據綁定表達式。 |
| TemplateControl.Eval (String, String) | 使用用于顯示結果的指定格式字符串計算數據綁定表達式。 |
如果細心的你查看TemplateControl類的基類Control類,你就會發現其實Control類并沒有提供Eval,XPath,XPathSelect等方法。所以Eval,XPath等方法最終是在TemplateControl類中實現的。
現在,終于找到了Eval,XPath等數據綁定方法的來源了。
Eval,XPath等方法是.NET 2.0新增的方法。在.NET 1.1時代我們經常用的是DateBinder.Eval方法。形如: <%#DataBind.Eval(Container.DataItem,"字段名")?%>
<%#DataBind.Eval(Container.DataItem,"字段名","{0:c}")?%> Eval的出現其實就是為了簡化DataBinder.Eval方法的寫法從而代替它。
在ASP.NET 2.0中及以上,當我們調用Eval時,Eval?方法會使用GetDataItem方法調用DataBinder.Eval方法計算表達式的值。要想理解這句話,就算查邊MSDN也一頭霧水,除非我們知道Eval方法的源代碼,否則根本找不到蛛絲馬跡。這里就要用到反射了。我們通過反射獲得了Eval方法的源代碼: protected???internal???object???Eval(string???expression)???
??{???
????????????this.CheckPageExists();???
????????????return???DataBinder.Eval(this.Page.GetDataItem(),???expression);???
??}??? 終于見到GetDataItem()方法了,其實它就是Page類的一個方法,也是.NET 2.0新增一個方法。GetDataItem()方法的作用是為了獲得Container.DataItem,它是.NET 2.0中用來代替Container.DataItem的,如果你曾經用Repeater和DataList等綁定過數組或者ArrayList等,你就會發現<%#GetDataItem()%>和<%#Container.DataItem%>等價。同時,可以肯定:Eval方法在低層上確實是調用DataBinder.Eval方法實現數據綁定的。其中“this.CheckPageExists();”?是檢查調用的時候有沒有Page對象的,如果沒有則會拋出一個異常。?
要弄清Eval是怎么工作的,GetDataItem()方法的低層實現我們也要用反射來獲取: public?object?GetDataItem()
????{
????????if?((this._dataBindingContext?==?null)?||?(this._dataBindingContext.Count?==?0))
????????{
????????????throw?new?InvalidOperationException(SR.GetString("Page_MissingDataBindingContext"));
????????}
????????return?this._dataBindingContext.Peek();
????}??? 我們從GatDataItem()方法中看到“return ? this._dataBindingContext.Peek();”很快就猜想_dataBindingContext是不是一個堆棧呢?事實它就是一個堆棧!通過反射查看源代碼我們得出:_dataBindingContext是一個Stack類型對象。所以它有Peek方法。“return ? this._dataBindingContext.Peek();?”正是把堆棧頂部的元素返回。而if語句是用來判斷這個堆棧是否已經存在或者是否已經有元素存在,如果if不成立,就會拋出一個異常。
從上面的分析我們知道:_dataBindingContext堆棧的作用是通過GetDataItem()方法這個橋梁向Eval方法提供Container.DateItem。用逆向思維來理解上面這句話:Eval方法可以自動計算出Container.DataItem,原因就是從dataBindingContext堆棧來獲取Container.DataItem;這也就為什么Eval方法能夠知道形如<%#Eval"字段名"%>中字段名隸屬于哪個數據項的屬性的原因;同時我們也知道.NET 2.0中的Eval在本質上的實現并沒有拋棄Container.DataItem,而Container.DataItem在2.0時代也沒有消失。
那么_dataBindingContext這個保存Container.DataItem的堆棧是怎么建立的呢?
我們很快就想到每次綁定控件時候最后那條語句是什么:this.控件ID.DataBind();對就是DataBind()方法,DataBind()方法還有一個重載:DataBind(bool raiseOnDataBinding)。為_dataBindingContext這個堆棧壓入元素和彈出元素的方法正是用DataBind(bool flag)這個重載方法實現的。
DataBind(bool raiseOnDataBinding)在低層的實現: ?protected?virtual?void?DataBind(bool?raiseOnDataBinding)
????{
????????bool?flag1?=?false;//這個標志的用處在上下文中很容易推出來,如果有DataItem壓棧,則在后面出棧。???
????????if?(this.IsBindingContainer)//判斷控件是不是數據綁定容器,實際上就是判斷控件類是不是實現了INamingContainer???
????????{
????????????bool?flag2;
????????????object?obj1?=?DataBinder.GetDataItem(this,?out???flag2);//這個方法是判斷控件是不是有DataItem屬性,并把它取出來。???
????????????if?(flag2?&&?(this.Page?!=?null))//如果控件有DataItem???
????????????{
????????????????this.Page.PushDataBindingContext(obj1);//把DataItem壓棧,PushDataBindingContext就是調用_dataBindingContext的Push方法???
????????????????flag1?=?true;
????????????}
????????}
????????try
????????{
????????????if?(raiseOnDataBinding)//這里是判斷是不是觸發DataBinding事件的。???
????????????{
????????????????this.OnDataBinding(EventArgs.Empty);
????????????}
????????????this.DataBindChildren();//對子控件進行數據綁定,如果這個控件有DataItem,則上面會將DataItem壓入棧頂,這樣,在子控件里面調用Eval或者GetDataItem方法,就會把剛剛壓進去的DataItem給取出來。???
????????}
????????finally
????????{
????????????if?(flag1)//如果剛才有壓棧,則現在彈出來。???
????????????{
????????????????this.Page.PopDataBindingContext();//PopDataBindingContext就是調用_dataBindingContext的Pop方法???
????????????}
????????}
????}???
當我們執行到this.控件ID.DataBind();時候。在低層上就會調用這個重載的方法來準備包含DataItem的_DatBindingContext堆棧。
上面的代碼中提到了DataBinding事件,那么它一般什么時候被觸發呢?
1,如果用編程方式,那么在我們調用DataBind()方法時候自動觸發DataBinding事件。
2,如果我們用數據源控件(例如SqlDataSource等),當把控件綁定到數據源控件時候,這個事件就會自動觸發。
一般數據綁定表達式常常放在模板中循環顯示數據,例如Repeater和DataList等的模板。那么下面這個知識點應該知道:Repeater,DataList,FormView等控件必須使用模板,如果不使用模板,這些控件將無法顯示數據。而GridView,DetailsView,Menu等控件也支持模板,但顯示數據時不是必須的。而TreeView控件不支持模板。
注意:一般情況下,數據綁定表達式不會自動計算它的值,除非它所在的頁或者控件顯示調用DataBind()方法。DataBind()方法能夠將數據源綁定到被調用的服務器控件及其所有子控件,同時分析并計算數據綁定表達式的值。
終于寫的有點眉目了,好累!我們該回頭看看Eval方法調用的靜態DataBinder.Eval方法在低層的實現了。我把DataBinder類的源代碼作為附近提供下載。 二,執行效率
從“一”講述的低層實現。我們很容易來排序下面數據綁定表達式的執行效率 <%#Container.DataItem%>
<%#GetDataItem()%>
<%#Eval("字段名")%>
<%#DataBinder.Eval(Container.DataItem,"字段名")%>
<%#((DataRowView)Container.DataItem)["字段名"]?%>
<%#((Type)Container.DataItem).成員?%>
<%#((Type)GetDataItem()).成員?%> 1,效率最高應該是: <%#((Type)Container.DataItem).成員?%>
<%#Container.DataItem%>
<%#((DataRowView)Container.DataItem)["字段名"]?%>
2,效率排第二的是: <%#((Type)GetDataItem()).成員?%>
<%#GetDataItem()%>
3,效率最低的是: <%#Eval("字段名")%>
<%#DataBinder.Eval(Container.DataItem,"字段名")%> 其實按上面的排序有失公允,原因是這七種數據表達綁定形式運用的場合不是完全相同的。
使用場合大概如下:
1,
<%#Eval("字段名")%>
<%#DataBinder.Eval(Container.DataItem,"字段名")%>
它們的使用場合最廣,數據源可以為與數據庫有關的DataSet,DataTable,DataView。也可以為普通集合(例如:數組,ArrayList,HashTable等)和泛行集合(例如:List<T>,Dictionary<Tkey,Tvalue>等)。
注:它們2個永遠可以相互替換,至少目前是這樣,凡是可以用Eval方法的地方,就可以用DataBinder.Eval方法替換。從低層實現上,Eval比DataBinder.Eval方法效率稍低,原因是Eval方法對了調用GetDataItem()方法這一步。但最終都是通過DataBinder.Eval方法利用反射技術根據名稱查找屬性,從而計算出表達式的值,所以非常影響性能。
2,
<%#((DataRowView)Container.DataItem)["字段名"]?%>
它只能使用在數據源為與數據庫有關的Dataset,DatTable,DataView。這些數據源都實現了IListSource接口。其實從低層實現本質上來看,它和<%#((Type)Container.DataItem).成員?%>類似。
3,
<%#Container.DataItem%>
<%#GetDataItem()%>
<%#((Type)Container.DataItem).成員?%>
<%#((Type)GetDataItem()).成員?%>
這幾種形式估計大家最不常用。它們一般只使用與普通集合(例如:數組,ArrayList,HashTable)和泛行集合(例如:List<T>,Dictionary<Tkey,Tvalue>)。其實本質上就是實現了IList,ICollection,IEnumerable,IDictionary等以及這些接口對應的泛行接口的集合。IList接口和IDictionary接口的區別是,一個只有值,而另一個是鍵/值對,對應泛行形式也是這樣。而Array就對用List<T>,而HashTable就對應Dictionary<Tkey,Tvalue>。
附件:http://down.51cto.com/data/2352395
本文轉自terryli51CTO博客,原文鏈接:http://blog.51cto.com/terryli/150594?,如需轉載請自行聯系原作者
總結
以上是生活随笔為你收集整理的数据绑定表达式(下):.NET发现之旅(二)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Webex远程网络视频会议使用指南
- 下一篇: 由浅入深laravel教程 :中间件