浅谈.NET中的数据绑定表达式

    技术2022-05-12  46

    http://www.cnblogs.com/terryli/archive/2008/03/25/1120482.html

     

    数据绑定表达式必须包含在<%#和%>字符之间。格式如下:

    <tagprefix:tagname property='<%# data-binding expression %>' runat="server" />

     

    或者如下:

    <%# data-binding expression %>

    ASP.NET 支持分层数据绑定模型,数据绑定表达式使用 Eval 和 Bind 方法将数据绑定到控件,并将更改提交回数据库。Eval 方法是静态单向(只读)方法,所以Eval 函数用于单向(只读)绑定,该方法采用数据字段的值作为参数并将其作为字符串返回。Bind 方法支持读/写功能,所以Bind 函数用于双向(可更新)绑定。该方法可以检索数据绑定控件的值并将任何更改提交回数据库。XPath 方法支持对XML类型的数据源提供支持。数据绑定表达式都可以出现在页面的哪个位置呢?一,可以将数据绑定表达式包含在服务器控件或者普通的html元素的开始标记中属性名/属性值对的值侧。例如:

    <asp:TextBox ID="TextBox1" runat="server" Text='<%#数据绑定表达式%>' ></asp:TextBox><br />

    注意条款:此时数据的绑顶表达式可以是一个变量,也可以是一个带返回值的C#或者VB.NET方法,还可以是某个控件的某个属性的值,也可以是C#或者VB.NET对象的某个字段或者属性的值等等。当然也可以直接就是一个字符串,例如"hello"。如果此时的数据绑定表达式是Eval("数据库中某个表的某个字段")等,那么必须把TextBox1放在某个循环显示的控件的模板中才正确,否则会提示:Eval()、XPath() 和 Bind() 这类数据绑定方法只能在数据绑定控件的上下文中使用。其实就是想让你把TextBox1放在像Repeater,DataList,GridView这样的控件的模板中。二,数据绑定绑定表达式包含在在页面中的任何位置。例如:

    <form id="form1" runat="server">      <div>       <%#Eval("数据绑定表达式1")%>        <%#Eval("数据绑定表达式2")%>             </div>  </form>

    同样遵循"一"的注意条款。如果此时的数据绑定表达式是Eval("数据库中某个表的某个字段")等,那么必须把 <%#Eval("数据绑定表达式1")%>   <%#Eval("数据绑定表达式2")%>  放在像Repeater,DataList,GridView这样的控件的模板中。三,可以将数据绑定表达式包含在Javascript代码中,从而实现在Javascript中调用C#或者VB.NET的方法。例如:Deafult2.aspx:

    <%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default2.aspx.cs" Inherits="Default2" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" > <head runat="server">      <title>无标题页</title>      <script language ="javascript" type="text/javascript">     function GetStr()    {       var a;        a = '';        a='<%#CSharpToJavascript()%>'         //调用c#的方法       alert(a);    }     </script>     </head> <body>      <form id="form1" runat="server">      <div>          <input id="Button1" type="button" value="Javascript调用c#的方法!" onclick="GetStr()" /</div>      </form> </body> </html>

    Default2.cs:

    using  System;using  System.Data;using  System.Configuration;using  System.Collections;using  System.Web;using  System.Web.Security;using  System.Web.UI;using  System.Web.UI.WebControls;using  System.Web.UI.WebControls.WebParts;using  System.Web.UI.HtmlControls;public partial class  Default2 : System.Web.UI.Page{    protected void Page_Load(object  sender, EventArgs e)    {        Page.DataBind();//方法有返回值的要先绑定,才能实现Javascript调用c#的方法!     }    public string  CSharpToJavascript()    {        return "Javascript调用c#的方法!" ;    }}

    数据绑定表达式都可以是什么类型呢?1,可以是一个变量例如:<asp:Label ID="Label1" runat="server" Text="<%#变量名%>"></asp:Label>2,可以是服务器控件的属性值例如: <asp:Label ID="Label1" runat="server" Text="<%#TextBox2.Text %>"></asp:Label>3,可以是一个数组等集合对象例如把一个数组绑定到列表控件,例如ListBox等,或者Repeater,DataList,GridView这样的控件等,此时只需要把属性DataSource='<%# 数组名%>' 。4,可以是一个表达式例如:Person是一个对象,Name和City是它的2个属性,则数据绑定表达式可以这样写:<%#(Person.Name + " " + Person.City)%>。5,可以是一个方法例如:<%#GetUserName()%>。GetUserName()是一个已经定义的C#方法,一般要求有返回值。6,可以是用Eval,DateBinder.Eval取得的数据表的字段,这个是最常见的了,不再举例。注意:如果数据绑定表达式作为属性的值,只要数据绑定表达式中没有出现双引号,那么<%#数据绑定表达式%>的最外层用双引号或者单引号都可以。如果数据绑定表达式中出现双引号,则<%#数据绑定表达式%>的最外层最好要用单引号。与数据库有关而且绑定到DataView,DataTable,DataSet 等数据源的数据绑定表达式都有那些?1,<%#DataBinder.Eval(Container.DataItem,"字段名")%>      <%#DataBinder.Eval(Container.DataItem,"字段名","{0:c}") %>     还有2种不常用的:      <%# DataBinder.Eval(Container,"DataItem.字段名")%>       <%# DataBinder.Eval(Container,"DataItem.字段名",{0:c})%>       Container.DataItem相当于数据库中某个表或者视图中的一行记录,而一行可以有很多列。      最后一个参数和 String.Format的形式一样。例如c代表货币,p代表百分号,d代表短日期格式显示,f代表浮点数现实,f3代表小数点后三为,一次类推。         使用三目运算符?:的例子: <%# DataBinder.Eval(Container.DataItem, "字段名").ToString().Trim().Length>16?DataBinder.Eval(Container.DataItem, "字段名").ToString().Trim().Substring(0,16):DataBinder.Eval(Container.DataItem, "字段名").ToString().Trim() %>2,<%#Eval("字段名")%>     <%#Eval("字段名","{0:c}")%>     .NET 2.0新出现的一个方法。和DataBinder.Eval()等价。     最后一个参数和

    String.Format的形式一样。例如c代表货币,p代表百分号,d代表短日期格式显示,f代表浮点数现实,f3代表小数点后三位,一次类推。

                   < %#string.Format ("{0:yyyy-MM-dd dddd}",Eval("stuBirth"))% >              < %# DataBinder.Eval (Container.DataItem,"stuBirth","{0:yyyy-MM-dd}")% >

        

    3, <%#((DataRowView)Container.DataItem)["字段名"] %>      <%# string.Format("{0:c}", ((DataRowView)Container.DataItem)["字段名"])%>      Container.DataItem相当于数据库中某个表或者视图中的一行记录,而一行可以有很多列。        用 String.Format方法设定字段的显示样式。例如c代表货币,p代表百分号,d代表短日期格式显示,f代表浮点数现实,f3代表小数点后三为,一次类推。              类型转换例子:<%# (int)((DataRowView)Container.DataItem)["字段名1"]*(int)((DataRowView)Container.DataItem)["字段名2"]%>    意思是2个字段相乘。    

    上面三种绑定方法的效率:Eval方法执行时候会调用DataBinder.Eval方法,DataBinder.Eval方法在运行时使用反射执行后期绑定计算,会导致性能明显下降。所以会导致性能明显下降。所以三者中<%#((DataRowView)Container.DataItem)["字段名"] %>的性能最好。

    这一节继续来谈.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类的源代码贴出来:

    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, nullnullnew 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>。

     


    最新回复(0)