這一節繼續來談NET中的數據綁定表達式
本節涉及的內容如下
一數據綁定方法的來源以及在低層上的實現
二數據綁定方法的執行效率排序
<%#ContainerDataItem%>
<%#GetDataItem()%>
<%#Eval(字段名)%>
<%#DataBinderEval(ContainerDataItem字段名)%>
<%#((DataRowView)ContainerDataItem)[字段名] %>
<%#((Type)ContainerDataItem)成員 %>
<%#((Type)GetDataItem())成員 %>
上面七種綁定形式以及它們的變幻形式都用過嗎?性能怎麼排序?
復習一下第一節我們主要談了數據綁定表達式的各種形式在ASPNET頁面中出現的位置以及我們常綁定到與數據庫有關的DataViewDataTableDataSet 等數據源的數據綁定表達式的各種形式
你有沒有對Eval方法和DataBinderEval方法好奇過?
在NET中我們經常用Eval方法在RepeaterDataListGridView等循環控件中綁定數據Eval方法和DataBinderEval方法在低層是怎麼實現的?它們到底有什麼千絲萬縷的關系?
一來源實現
我們常用的Eval方法其實是Page類的一個靜態單向只讀方法而且它是一個受保護的方法實際上Page類的Eval方法是繼承自TemplateControl類的TemplateControl 類是一個抽象類它為Page 類和 UserControl 類提供通用屬性和方法我們先來看一下繼承家譜
SystemObject
SystemWebUIControl
SystemWebUITemplateControl
SystemWebUIPage
SystemWebUIUserControl
Eval方法就是TemplateControl類的方法它有兩種形式
名稱
說明
TemplateControlEval (String)
計算數據綁定表達式
TemplateControlEval (String String)
使用用於顯示結果的指定格式字符串計算數據綁定表達式
事實上TemplateControl類還提供了XPath方法和XPathSelect方法供Page類和UserControl繼承這個方法是和XML數據源有關的綁定方法
如果細心的你查看TemplateControl類的基類Control類你就會發現其實Control類並沒有提供EvalXPathXPathSelect等方法所以EvalXPath等方法最終是在TemplateControl類中實現的
現在終於找到了EvalXPath等數據綁定方法的來源了
EvalXPath等方法是NET 新增的方法在NET 時代我們經常用的是DateBinderEval方法形如
<%#DataBindEval(ContainerDataItem字段名) %>
<%#DataBindEval(ContainerDataItem字段名{c}) %>
Eval的出現其實就是為了簡化DataBinderEval方法的寫法從而代替它
在ASPNET 中及以上當我們調用Eval時Eval 方法會使用GetDataItem方法調用DataBinderEval方法計算表達式的值要想理解這句話就算查邊MSDN也一頭霧水除非我們知道Eval方法的源代碼否則根本找不到蛛絲馬跡這裡就要用到反射了我們通過反射獲得了Eval方法的源代碼
protected internal object Eval(string expression)
{
thisCheckPageExists()
return DataBinderEval(thisPageGetDataItem() expression)
}
終於見到GetDataItem()方法了其實它就是Page類的一個方法也是NET 新增一個方法GetDataItem()方法的作用是為了獲得ContainerDataItem它是NET 中用來代替ContainerDataItem的如果你曾經用Repeater和DataList等綁定過數組或者ArrayList等你就會發現<%#GetDataItem()%>和<%#ContainerDataItem%>等價同時可以肯定Eval方法在低層上確實是調用DataBinderEval方法實現數據綁定的其中thisCheckPageExists() 是檢查調用的時候有沒有Page對象的如果沒有則會拋出一個異常
要弄清Eval是怎麼工作的GetDataItem()方法的低層實現我們也要用反射來獲取
public object GetDataItem()
{
if ((this_dataBindingContext == null) || (this_dataBindingContextCount == ))
{
throw new InvalidOperationException(SRGetString(Page_MissingDataBindingContext))
}
return this_dataBindingContextPeek()
}
我們從GatDataItem()方法中看到return this_dataBindingContextPeek()很快就猜想_dataBindingContext是不是一個堆棧呢?事實它就是一個堆棧!通過反射查看源代碼我們得出_dataBindingContext是一個Stack類型對象所以它有Peek方法return this_dataBindingContextPeek() 正是把堆棧頂部的元素返回而if語句是用來判斷這個堆棧是否已經存在或者是否已經有元素存在如果if不成立就會拋出一個異常
從上面的分析我們知道_dataBindingContext堆棧的作用是通過GetDataItem()方法這個橋梁向Eval方法提供ContainerDateItem用逆向思維來理解上面這句話Eval方法可以自動計算出ContainerDataItem原因就是從dataBindingContext堆棧來獲取ContainerDataItem這也就為什麼Eval方法能夠知道形如<%#Eval字段名%>中字段名隸屬於哪個數據項的屬性的原因同時我們也知道NET 中的Eval在本質上的實現並沒有拋棄ContainerDataItem而ContainerDataItem在時代也沒有消失
那麼_dataBindingContext這個保存ContainerDataItem的堆棧是怎麼建立的呢?
我們很快就想到每次綁定控件時候最後那條語句是什麼this控件IDDataBind()對就是DataBind()方法DataBind()方法還有一個重載DataBind(bool raiseOnDataBinding)為_dataBindingContext這個堆棧壓入元素和彈出元素的方法正是用DataBind(bool flag)這個重載方法實現的
DataBind(bool raiseOnDataBinding)在低層的實現
protected virtual void DataBind(bool raiseOnDataBinding)
{
bool flag = false;//這個標志的用處在上下文中很容易推出來如果有DataItem壓棧則在後面出棧
if (thisIsBindingContainer)//判斷控件是不是數據綁定容器實際上就是判斷控件類是不是實現了INamingContainer
{
bool flag;
object obj = DataBinderGetDataItem(this out flag);//這個方法是判斷控件是不是有DataItem屬性並把它取出來
if (flag && (thisPage != null))//如果控件有DataItem
{
thisPagePushDataBindingContext(obj);//把DataItem壓棧PushDataBindingContext就是調用_dataBindingContext的Push方法
flag = true;
}
}
try
{
if (raiseOnDataBinding)//這裡是判斷是不是觸發DataBinding事件的
{
thisOnDataBinding(EventArgsEmpty);
}
thisDataBindChildren();//對子控件進行數據綁定如果這個控件有DataItem則上面會將DataItem壓入棧頂這樣在子控件裡面調用Eval或者GetDataItem方法就會把剛剛壓進去的DataItem給取出來
}
finally
{
if (flag)//如果剛才有壓棧則現在彈出來
{
thisPagePopDataBindingContext();//PopDataBindingContext就是調用_dataBindingContext的Pop方法
}
}
}
當我們執行到this控件IDDataBind()時候在低層上就會調用這個重載的方法來准備包含DataItem的_DatBindingContext堆棧
上面的代碼中提到了DataBinding事件那麼它一般什麼時候被觸發呢?
1如果用編程方式那麼在我們調用DataBind()方法時候自動觸發DataBinding事件
2如果我們用數據源控件(例如SqlDataSource等)當把控件綁定到數據源控件時候這個事件就會自動觸發
一般數據綁定表達式常常放在模板中循環顯示數據例如Repeater和DataList等的模板那麼下面這個知識點應該知道RepeaterDataListFormView等控件必須使用模板如果不使用模板這些控件將無法顯示數據而GridViewDetailsViewMenu等控件也支持模板但顯示數據時不是必須的而TreeView控件不支持模板
注意一般情況下數據綁定表達式不會自動計算它的值除非它所在的頁或者控件顯示調用DataBind()方法DataBind()方法能夠將數據源綁定到被調用的服務器控件及其所有子控件同時分析並計算數據綁定表達式的值
終於寫的有點眉目了好累!我們該回頭看看Eval方法調用的靜態DataBinderEval方法在低層的實現了我把DataBinder類的源代碼作為附近提供下載
二執行效率
從一講述的低層實現我們很容易來排序下面數據綁定表達式的執行效率
<%#ContainerDataItem%>
<%#GetDataItem()%>
<%#Eval(字段名)%>
<%#DataBinderEval(ContainerDataItem字段名)%>
<%#((DataRowView)ContainerDataItem)[字段名] %>
<%#((Type)ContainerDataItem)成員 %>
<%#((Type)GetDataItem())成員 %>
效率最高應該是
<%#((Type)ContainerDataItem)成員 %>
<%#ContainerDataItem%>
<%#((DataRowView)ContainerDataItem)[字段名] %>
效率排第二的是
<%#((Type)GetDataItem())成員 %>
<%#GetDataItem()%>
效率最低的是
<%#Eval(字段名)%>
<%#DataBinderEval(ContainerDataItem字段名)%>
其實按上面的排序有失公允原因是這七種數據表達綁定形式運用的場合不是完全相同的
使用場合大概如下
|<%#Eval(字段名)%>
<%#DataBinderEval(ContainerDataItem字段名)%>
它們的使用場合最廣數據源可以為與數據庫有關的DataSetDataTableDataView也可以為普通集合(例如數組ArrayListHashTable等)和泛行集合(例如List<T>Dictionary<TkeyTvalue>等)
注它們個永遠可以相互替換至少目前是這樣凡是可以用Eval方法的地方就可以用DataBinderEval方法替換從低層實現上Eval比DataBinderEval方法效率稍低原因是Eval方法對了調用GetDataItem()方法這一步但最終都是通過DataBinderEval方法利用反射技術根據名稱查找屬性從而計算出表達式的值所以非常影響性能
<%#((DataRowView)ContainerDataItem)[字段名] %>
它只能使用在數據源為與數據庫有關的DatasetDatTableDataView這些數據源都實現了IListSource接口其實從低層實現本質上來看它和<%#((Type)ContainerDataItem)成員 %>類似
<%#ContainerDataItem%>
<%#GetDataItem()%>
<%#((Type)ContainerDataItem)成員 %>
<%#((Type)GetDataItem())成員 %>
這幾種形式估計大家最不常用它們一般只使用與普通集合(例如數組ArrayListHashTable)和泛行集合(例如List<T>Dictionary<TkeyTvalue>)其實本質上就是實現了IListICollectionIEnumerableIDictionary等以及這些接口對應的泛行接口的集合IList接口和IDictionary接口的區別是一個只有值而另一個是鍵/值對對應泛行形式也是這樣而Array就對用List<T>而HashTable就對應Dictionary<TkeyTvalue>
From:http://tw.wingwit.com/Article/program/net/201311/11866.html