[转]浅谈.NET中的数据绑定表达式(二)

本节涉及的内容如下:
1,数据绑定方法的来源以及在低层上的实现。
2,数据绑定方法的执行效率排序。
<%#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类还提供了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(thisout   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类的源代码贴出来:

ContractedBlock.gif ExpandedBlockStart.gif View Code
 
   
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;

[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 sealed class DataBinder
{
private static readonly char [] expressionPartSeparator = new char [] { ' . ' };
private static readonly char [] indexExprEndChars = new char [] { ' ] ' , ' ) ' };
private static readonly char [] indexExprStartChars = new char [] { ' [ ' , ' ( ' };

public static object Eval( object container, string expression)
{
if (expression == null )
{
throw new ArgumentNullException( " expression " );
}
if (container == null )
{
return null ;
}
string [] expressionParts = expression.Trim().Split(expressionPartSeparator);
return Eval(container, expressionParts);
}

private static object Eval( object container, string [] expressionParts)
{
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;
}

public static string Eval( object container, string expression, string format)
{
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();
}

public static object GetIndexedPropertyValue( object container, string expr)
{
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 });
}

public static string GetIndexedPropertyValue( object container, string propName, string format)
{
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();
}

public static object GetPropertyValue( object container, string propName)
{
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);
}

public static string GetPropertyValue( object container, string propName, string format)
{
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();
}
}
}


二,执行效率

从“一”讲述的低层实现。我们很容易来排序下面数据绑定表达式的执行效率
<%#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>。

这里举例<%#((Type)Container.DataItem).成员 %>在Repeater中绑定泛型List<T>的用法:

后台.cs代码:订单商品项模型绑定repeater

 
  
List < Model.OrderItems > listOrderItmes = new DAl.OrderItemsDAO().GetListArray( " OrderId = ' " + orderId + " ' order by Id desc " );
repList.DataSource
= listOrderItmes;
repList.DataBind();

前台.aspx代码:


<asp:Repeater ID="repList" runat="server">
<ItemTemplate>
<tr>
    <td class="td02"><a href='productcon.aspx?id=<%#Eval("ProductId") %>' target="_blank"><%#Eval("ProductName") %></a></td>
    <td align="right" valign="middle" class="td02">¥<%#((Model.OrderItems)Container.DataItem).UnitPrice %></td>
    <td valign="middle" class="td02 colorgreg"><%#((Model.OrderItems)Container.DataItem).Quantity%></td>
    <td align="right" class="td02">¥<%#((Model.OrderItems)Container.DataItem).TotalPrice %></td>
</tr>
</ItemTemplate>
</asp:Repeater>

 

 

转自:http://www.cnblogs.com/terryli/archive/2008/03/27/1125934.html

转载于:https://www.cnblogs.com/fishtreeyu/archive/2011/03/11/1980346.html

ASP.NET 数据绑定表达式用于将数据绑定到网页控件或服务器控件上,以便在页面渲染时显示相应的数据。它使用一种特殊的语法,用于在页面上嵌入动态生成的内容。 在 ASP.NET Web Forms 数据绑定表达式通常使用尖括号(<%# %>)包围。以下是一些常见的数据绑定表达式示例: 1. 绑定控件属性: ``` <asp:Label ID="lblName" runat="server" Text='<%# Eval("Name") %>'></asp:Label> ``` 上述代码将绑定一个 Label 控件的 Text 属性到数据的 "Name" 字段。 2. 绑定控件的可见性: ``` <asp:Panel ID="pnlStatus" runat="server" Visible='<%# Convert.ToBoolean(Eval("IsActive")) %>'> <!-- 控件内容 --> </asp:Panel> ``` 上述代码将根据数据的 "IsActive" 字段的值确定 Panel 控件是否显示。 3. 绑定控件的样式: ``` <asp:Button ID="btnSubmit" runat="server" CssClass='<%# (bool)Eval("IsEnabled") ? "enabled" : "disabled" %>' Text="Submit" /> ``` 上述代码将根据数据的 "IsEnabled" 字段的值为按钮控件应用不同的 CSS 类。 除了 Eval() 方法,还有其他常用的数据绑定表达式方法,如 Bind() 和 XPath()。Eval() 方法用于绑定使用简单语法的字段,而 Bind() 方法更适用于复杂的绑定场景。XPath() 方法可用于执行 XPath 查询。 需要注意的是,数据绑定表达式只能在绑定控件或服务器控件内部使用,并且需要在页面的 Page_Load 或 DataBound 事件调用 DataBind() 方法以实现数据绑定
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值