这一节继续来谈.NET中的数据绑定表达式。 本节涉及的内容如下: 复习一下:第一节我们主要谈了数据绑定表达式的各种形式,在ASP.NET页面中出现的位置,以及我们常绑定到与数据库有关的DataView,DataTable,DataSet 等数据源的数据绑定表达式的各种形式。 你有没有对Eval方法和DataBinder.Eval方法好奇过? 一,来源、实现。 System.Object Eval方法就是TemplateControl类的方法,它有两种形式: 事实上TemplateControl类还提供了XPath方法和XPathSelect方法供Page类和UserControl继承。这2个方法是和XML数据源有关的绑定方法。 protected internal object Eval(string 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()方法的低层实现我们也要用反射来获取: 我们从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的堆栈是怎么建立的呢? protected virtual void DataBind(bool raiseOnDataBinding) 当我们执行到this.控件ID.DataBind();时候。在低层上就会调用这个重载的方法来准备包含DataItem的_DatBindingContext堆栈。 [PermissionSet(SecurityAction.LinkDemand, XML="<PermissionSet class=\\"System.Security.PermissionSet\\"\\r\\n version=\\"1\\">\\r\\n <IPermission class=\\"System.Web.AspNetHostingPermission, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089\\"\\r\\n version=\\"1\\"\\r\\n Level=\\"Minimal\\"/>\\r\\n</PermissionSet>\\r\\n")] public static object Eval(object container, string expression) private static object Eval(object container, string[] expressionParts) public static string Eval(object container, string expression, string format) public static object GetIndexedPropertyValue(object container, string expr) public static string GetIndexedPropertyValue(object container, string propName, string format) public static object GetPropertyValue(object container, string propName) public static string GetPropertyValue(object container, string propName, string format) 二,执行效率 从“一”讲述的低层实现。我们很容易来排序下面数据绑定表达式的执行效率 <%#((Type)Container.DataItem).成员 %> <%#((Type)GetDataItem()).成员 %> 使用场合大概如下: 3, 先写到这里吧,实在太累了。 相关链接:浅谈.NET中的数据绑定表达式
\n
\n
1,数据绑定方法的来源以及在低层上的实现。
2,数据绑定方法的执行效率排序。
<%#Container.DataItem%>
<%#GetDataItem()%>
<%#Eval(“字段名”)%>
<%#DataBinder.Eval(Container.DataItem,”字段名”)%>
<%#((DataRowView)Container.DataItem)["字段名"] %>
<%#((Type)Container.DataItem).成员 %>
<%#((Type)GetDataItem()).成员 %>
上面七种绑定形式以及它们的变幻形式都用过吗?性能怎么排序?
\n
\n
在.NET2.0中我们经常用Eval方法在Repeater,DataList,GridView等循环控件中绑定数据,Eval方法和DataBinder.Eval方法在低层是怎么实现的?它们到底有什么千丝万缕的关系?
\n
我们常用的Eval方法其实是Page类的一个静态单向只读方法,而且它是一个受保护的方法。实际上Page类的Eval方法是继承自TemplateControl类的。TemplateControl 类是一个抽象类,它为Page 类和 UserControl 类提供通用属性和方法。我们先来看一下继承家谱:
\n
System.Web.UI.Control
System.Web.UI.TemplateControl
System.Web.UI.Page
System.Web.UI.UserControl
\n
名称 说明
TemplateControl.Eval (String) 计算数据绑定表达式。
TemplateControl.Eval (String, String) 使用用于显示结果的指定格式字符串计算数据绑定表达式。
\n
如果细心的你查看TemplateControl类的基类Control类,你就会发现其实Control类并没有提供Eval,XPath,XPathSelect等方法。所以Eval,XPath等方法最终是在TemplateControl类中实现的。
现在,终于找到了Eval,XPath等数据绑定方法的来源了。
Eval,XPath等方法是.NET 2.0新增的方法。在.NET 1.1时代我们经常用的是DateBinder.Eval方法。形如:
\n
<%#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方法的源代码:
\n
{
this.CheckPageExists();
return DataBinder.Eval(this.Page.GetDataItem(), expression);
}
\n
\n
\n
public object GetDataItem()
{
if ((this._dataBindingContext == null) || (this._dataBindingContext.Count == 0))
{
throw new InvalidOperationException(SR.GetString(“Page_MissingDataBindingContext”));
}
return this._dataBindingContext.Peek();
}
\n
\n
\n
我们很快就想到每次绑定控件时候最后那条语句是什么:this.控件ID.DataBind();对就是DataBind()方法,DataBind()方法还有一个重载:DataBind(bool raiseOnDataBinding)。为_dataBindingContext这个堆栈压入元素和弹出元素的方法正是用DataBind(bool flag)这个重载方法实现的。
DataBind(bool raiseOnDataBinding)在低层的实现:
\n
{
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方法
}
}
}
\n
上面的代码中提到了DataBinding事件,那么它一般什么时候被触发呢?
1,如果用编程方式,那么在我们调用DataBind()方法时候自动触发DataBinding事件。
2,如果我们用数据源控件(例如SqlDataSource等),当把控件绑定到数据源控件时候,这个事件就会自动触发。
一般数据绑定表达式常常放在模板中循环显示数据,例如Repeater和DataList等的模板。那么下面这个知识点应该知道:Repeater,DataList,FormView等控件必须使用模板,如果不使用模板,这些控件将无法显示数据。而GridView,DetailsView,Menu等控件也支持模板,但显示数据时不是必须的。而TreeView控件不支持模板。
注意:一般情况下,数据绑定表达式不会自动计算它的值,除非它所在的页或者控件显示调用DataBind()方法。DataBind()方法能够将数据源绑定到被调用的服务器控件及其所有子控件,同时分析并计算数据绑定表达式的值。
终于写的有点眉目了,好累!我们该回头看看Eval方法调用的静态DataBinder.Eval方法在低层的实现了。我把DataBinder类的源代码贴出来:
\n
namespace System.Web.UI
{
using System;
using System.Collections;
using System.ComponentModel;
using System.Globalization;
using System.Reflection;
using System.Security.Permissions;
using System.Web;
\n
public sealed class DataBinder
{
private static readonly char[] expressionPartSeparator = new char[] { ‘.’ };
private static readonly char[] indexExprEndChars = new char[] { ‘]’, ‘)’ };
private static readonly char[] indexExprStartChars = new char[] { ‘[\', \'(\' };
\n
{
if (expression == null)
{
throw new ArgumentNullException("expression");
}
if (container == null)
{
return null;
}
string[] expressionParts = expression.Trim().Split(expressionPartSeparator);
return Eval(container, expressionParts);
}
\n
{
object propertyValue = container;
for (int i = 0; (i < expressionParts.Length) && (propertyValue != null); i++)
{
string propName = expressionParts[i];
if (propName.IndexOfAny(indexExprStartChars) < 0)
{
propertyValue = GetPropertyValue(propertyValue, propName);
}
else
{
propertyValue = GetIndexedPropertyValue(propertyValue, propName);
}
}
return propertyValue;
}
\n
{
object obj2 = Eval(container, expression);
if ((obj2 == null) || (obj2 == DBNull.Value))
{
return string.Empty;
}
if ((format != null) && (format.Length != 0))
{
return string.Format(format, obj2);
}
return obj2.ToString();
}
\n
{
if (container == null)
{
throw new ArgumentNullException(“container”);
}
if ((expr == null) || (expr.Length == 0))
{
throw new ArgumentNullException(“expr”);
}
object obj2 = null;
bool flag = false;
int length = expr.IndexOfAny(indexExprStartChars);
int num2 = expr.IndexOfAny(indexExprEndChars, length + 1);
if (((length < 0) || (num2 < 0)) || (num2 == (length + 1)))
{
throw new ArgumentException(HttpRuntime.FormatResourceString(“DataBinder_Invalid_Indexed_Expr”, expr));
}
string propName = null;
object obj3 = null;
string s = expr.Substring(length + 1, (num2 – length) – 1).Trim();
if (length != 0)
{
propName = expr.Substring(0, length);
}
if (s.Length != 0)
{
if (((s[0] == ‘”‘) && (s[s.Length - 1] == ‘”‘)) || ((s[0] == ‘\\”) && (s[s.Length - 1] == ‘\\”)))
{
obj3 = s.Substring(1, s.Length – 2);
}
else if (char.IsDigit(s[0]))
{
try
{
obj3 = int.Parse(s, CultureInfo.InvariantCulture);
flag = true;
}
catch (Exception)
{
obj3 = s;
}
}
else
{
obj3 = s;
}
}
if (obj3 == null)
{
throw new ArgumentException(HttpRuntime.FormatResourceString(“DataBinder_Invalid_Indexed_Expr”, expr));
}
object propertyValue = null;
if ((propName != null) && (propName.Length != 0))
{
propertyValue = GetPropertyValue(container, propName);
}
else
{
propertyValue = container;
}
if (propertyValue == null)
{
return obj2;
}
if ((propertyValue is Array) && flag)
{
return ((object[]) propertyValue)[(int) obj3];
}
if ((propertyValue is IList) && flag)
{
return ((IList) propertyValue)[(int) obj3];
}
PropertyInfo info = propertyValue.GetType().GetProperty(“Item”, BindingFlags.Public | BindingFlags.Instance, null, null, new Type[] { obj3.GetType() }, null);
if (info == null)
{
throw new ArgumentException(HttpRuntime.FormatResourceString(“DataBinder_No_Indexed_Accessor”, propertyValue.GetType().FullName));
}
return info.GetValue(propertyValue, new object[] { obj3 });
}
\n
{
object indexedPropertyValue = GetIndexedPropertyValue(container, propName);
if ((indexedPropertyValue == null) || (indexedPropertyValue == DBNull.Value))
{
return string.Empty;
}
if ((format != null) && (format.Length != 0))
{
return string.Format(format, indexedPropertyValue);
}
return indexedPropertyValue.ToString();
}
\n
{
if (container == null)
{
throw new ArgumentNullException(“container”);
}
if ((propName == null) || (propName.Length == 0))
{
throw new ArgumentNullException(“propName”);
}
PropertyDescriptor descriptor = TypeDescriptor.GetProperties(container).Find(propName, true);
if (descriptor == null)
{
throw new HttpException(HttpRuntime.FormatResourceString(“DataBinder_Prop_Not_Found”, container.GetType().FullName, propName));
}
return descriptor.GetValue(container);
}
\n
{
object propertyValue = GetPropertyValue(container, propName);
if ((propertyValue == null) || (propertyValue == DBNull.Value))
{
return string.Empty;
}
if ((format != null) && (format.Length != 0))
{
return string.Format(format, propertyValue);
}
return propertyValue.ToString();
}
}
}
\n
\n
<%#Container.DataItem%>
<%#GetDataItem()%>
<%#Eval(“字段名”)%>
<%#DataBinder.Eval(Container.DataItem,”字段名”)%>
<%#((DataRowView)Container.DataItem)["字段名"] %>
<%#((Type)Container.DataItem).成员 %>
<%#((Type)GetDataItem()).成员 %>
1,效率最高应该是:
\n
<%#Container.DataItem%>
<%#((DataRowView)Container.DataItem)["字段名"] %>
2,效率排第二的是:
\n
<%#GetDataItem()%>
3,效率最低的是:
<%#Eval(“字段名”)%>
<%#DataBinder.Eval(Container.DataItem,”字段名”)%>
其实按上面的排序有失公允,原因是这七种数据表达绑定形式运用的场合不是完全相同的。
\n
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).成员 %>类似。
\n
<%#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>。
\n
抽时间把不常用的数据绑定表达式运用一下,举点例子。同时把Bind和XPan绑定方法演示下。
\n
来源:http://www.cnblogs.com/terryli