数据绑定表达式(上):.NET发现之旅(一)

2009-06-30 10:29:06 来源:网络转载 作者:佚名 共有评论(0)条 浏览次数:859

  作为.NET平台软件开发者,我们频繁与各种各样的数据交互,这些数据常常来源于文本、自定义类型、XML、数据库等,访问这些数据有很多方法,而数据绑定表达式便是其中最常用也是最实用的方法之一。我用2篇博文,尽量说透。NET平台数据绑定表达的来源,使用方法,底层原理,效率等。另外这2篇博文我最初发表于博客园。

   一,概要

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

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

  或者如下:

<%# data-binding expression %>

 ASP.NET 支持分层数据绑定模型,数据绑定表达式使用 Eval 和 Bind 方法将数据绑定到控件,并将更改提交回数据库。

 Eval 方法是静态单向(只读)方法,所以Eval 函数用于单向(只读)绑定,该方法采用数据字段的值作为参数并将其作为字符串返回。

 Bind 方法支持读/写功能,所以Bind 函数用于双向(可更新)绑定。该方法可以检索数据绑定控件的值并将任何更改提交回数据库。

  XPath 方法支持对XML类型的数据源提供支持。

 二,数据绑定表达式出现的位置

 1,可以将数据绑定表达式包含在服务器控件或者普通的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这样的控件的模板中。   2,数据绑定绑定表达式包含在在页面中的任何位置。例如:

<form id="form1" runat="server">

<div>

<%#Eval("数据绑定表达式1")%>

<%#Eval("数据绑定表达式2")%>

</div>

</form>

  此时数据的绑顶表达式可以是一个变量,也可以是一个带返回值的C#或者VB.NET方法,还可以是某个控件的某个属性的值,也可以是C#或者VB.NET对象的某个字段或者属性的值等等。当然也可以直接就是一个字符串,例如"hello".

 如果此时的数据绑定表达式是Eval("数据库中某个表的某个字段")等,那么必须把<%#Eval("数据绑定表达式1")%>  <%#Eval("数据绑定表达式2")%>  放在像Repeater,DataList,GridView这样的控件的模板中。

 3,可以将数据绑定表达式包含在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>

123

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 等数据源的数据绑定表达式都有那些?

,<%#DataBinder.Eval(Container.DataItem,"字段名")%>

<%#DataBinder.Eval(Container.DataItem,"字段名","{0:c}") %>

 还有2种不常用的:

<%# DataBinder.Eval(Container,"DataItem.字段名")%>

<%# DataBinder.Eval(Container,"DataItem.字段名",{0:c})%>

Container.DataItem相当于数据库中某个表或者视图中的一行记录,而一行可以有很多列。

 使用三目运算符?:的例子:

<%# 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("{0:yyyy-MM-dd dddd}",Eval("stuBirth"))%>

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

    使用三目运算符的例子:

  <%#(Eval("性别"))。ToString() =="True"?"男":"女"%>

 性别字段类型为:是/否(Access),bit(sql server)

 调用方法的例子:

<%# GetUserPhoto(Eval("PhotoPath")) %>

GetUserPhoto()的定义:

string GetUserPhoto(object photoPath)

{

if (photoPath == DBNull.Value)<%#((DataRowView)Container.DataItem)["字段名"] %>

绑定到DataView,DataTable,DataSet

{

return "<img src='Images/none.gif'>";

}

else

{

return "<img src='Upload/" +photoPath.ToString() + "'>";

}

}

123

 3, <%#((DataRowView)Container.DataItem)["字段名"] %>

<%# string.Format("{0:c}", ((DataRowView)Container.DataItem)["字段名"])%>

Container.DataItem相当于数据库中某个表或者视图中的一行记录,而一行可以有很多列。

 类型转换后相乘的例子:

<%# (int)((DataRowView)Container.DataItem)["字段名1"]*(int) ((DataRowView)Container.DataItem)["字段名2"]%>

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

数据绑定表达式(下):.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.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(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://www.rjgc.net/control/content/content.php?nid=11386

  作为.NET平台软件开发者,我们频繁与各种各样的数据交互,这些数据常常来源于文本、自定义类型、XML、数据库等,访问这些数据有很多方法,而数据绑定表达式便是其中最常用也是最实用的方法之一。我用2篇博文,尽量说透。NET平台数据绑定表达的来源,使用方法,底层原理,效率等。另外这2篇博文我最初发表于博客园。

   一,概要

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

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

  或者如下:

<%# data-binding expression %>

 ASP.NET 支持分层数据绑定模型,数据绑定表达式使用 Eval 和 Bind 方法将数据绑定到控件,并将更改提交回数据库。

 Eval 方法是静态单向(只读)方法,所以Eval 函数用于单向(只读)绑定,该方法采用数据字段的值作为参数并将其作为字符串返回。

 Bind 方法支持读/写功能,所以Bind 函数用于双向(可更新)绑定。该方法可以检索数据绑定控件的值并将任何更改提交回数据库。

  XPath 方法支持对XML类型的数据源提供支持。

 二,数据绑定表达式出现的位置

 1,可以将数据绑定表达式包含在服务器控件或者普通的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这样的控件的模板中。   2,数据绑定绑定表达式包含在在页面中的任何位置。例如:

<form id="form1" runat="server">

<div>

<%#Eval("数据绑定表达式1")%>

<%#Eval("数据绑定表达式2")%>

</div>

</form>

  此时数据的绑顶表达式可以是一个变量,也可以是一个带返回值的C#或者VB.NET方法,还可以是某个控件的某个属性的值,也可以是C#或者VB.NET对象的某个字段或者属性的值等等。当然也可以直接就是一个字符串,例如"hello".

 如果此时的数据绑定表达式是Eval("数据库中某个表的某个字段")等,那么必须把<%#Eval("数据绑定表达式1")%>  <%#Eval("数据绑定表达式2")%>  放在像Repeater,DataList,GridView这样的控件的模板中。

 3,可以将数据绑定表达式包含在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>

123

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 等数据源的数据绑定表达式都有那些?

,<%#DataBinder.Eval(Container.DataItem,"字段名")%>

<%#DataBinder.Eval(Container.DataItem,"字段名","{0:c}") %>

 还有2种不常用的:

<%# DataBinder.Eval(Container,"DataItem.字段名")%>

<%# DataBinder.Eval(Container,"DataItem.字段名",{0:c})%>

Container.DataItem相当于数据库中某个表或者视图中的一行记录,而一行可以有很多列。

 使用三目运算符?:的例子:

<%# 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("{0:yyyy-MM-dd dddd}",Eval("stuBirth"))%>

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

    使用三目运算符的例子:

  <%#(Eval("性别"))。ToString() =="True"?"男":"女"%>

 性别字段类型为:是/否(Access),bit(sql server)

 调用方法的例子:

<%# GetUserPhoto(Eval("PhotoPath")) %>

GetUserPhoto()的定义:

string GetUserPhoto(object photoPath)

{

if (photoPath == DBNull.Value)<%#((DataRowView)Container.DataItem)["字段名"] %>

绑定到DataView,DataTable,DataSet

{

return "<img src='Images/none.gif'>";

}

else

{

return "<img src='Upload/" +photoPath.ToString() + "'>";

}

}

123

 3, <%#((DataRowView)Container.DataItem)["字段名"] %>

<%# string.Format("{0:c}", ((DataRowView)Container.DataItem)["字段名"])%>

Container.DataItem相当于数据库中某个表或者视图中的一行记录,而一行可以有很多列。

 类型转换后相乘的例子:

<%# (int)((DataRowView)Container.DataItem)["字段名1"]*(int) ((DataRowView)Container.DataItem)["字段名2"]%>

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

数据绑定表达式(下):.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.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(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://www.rjgc.net/control/content/content.php?nid=11386

数据绑定表达式(上):.NET发现之旅(一)的更多相关文章

  1. 【翻译】WPF中的数据绑定表达式

    有很多文章讨论绑定的概念,并讲解如何使用StaticResources和DynamicResources绑定属性.这些概念使用WPF提供的数据绑定表达式.在本文中,让我们研究WPF提供的不同类型的数据 ...

  2. 【转载】ASP.NET 内联代码、内联表达式、数据绑定表达式使用方法罗列(形式就是常说的尖括号 百分号 等于号 井号)

    ASP.NET 内联代码.内联表达式.数据绑定表达式使用方法罗列(形式就是常说的尖括号 百分号 等于号 井号) 今天在做渭南电脑维修网的一个小功能时遇到了一些问题,因此特别列出,以备他日之用. 首先对 ...

  3. ASP.NET 内联代码、内联表达式、数据绑定表达式使用方法罗列(形式就是常说的尖括号 百分号 等于号 井号)

    今天在做渭南电脑维修网的一个小功能时遇到了一些问题,因此特别列出,以备他日之用. 首先对ASP.NET 内联代码.内联表达式.数据绑定表达式的概念进行罗列,详细概念以及基本的用法我就不在这里罗嗦了,请 ...

  4. ASP.NET中数据绑定表达式

    今天谈下.NET中的数据绑定表达式.数据绑定表达式必须包含在<%#和%>字符之间.格式如下: <tagprefix:tagname property='<%# data-bin ...

  5. [转帖]Docker Hub上镜像发现挖矿蠕虫病毒,已导致2000台主机感染

    Docker Hub上镜像发现挖矿蠕虫病毒,已导致2000台主机感染 https://www.kubernetes.org.cn/5951.html 本来想说可以用 official版本的镜像 但是一 ...

  6. DokuWiki的发现之旅

    ★DokuWiki介绍 Wiki是什么?是一种允许一群用户用简单的描述来创建和连接一组网页的社会计算系统,可以让人们在web的基础上对Wiki文本进行浏览.创建和更改,是一种人类的知识的网络系统,有助 ...

  7. 微信小程序 发现之旅(一)—— 项目搭建与页面跳转

    开发微信小程序需要注册一个小程序账号,具体流程可以参照官方教程: https://mp.weixin.qq.com/debug/wxadoc/dev/index.html 开通账户之后,在 “开发设置 ...

  8. 微信小程序 发现之旅(三)—— 组件之间的参数传递

    一.URL 传参 当使用 navigateTo() 方法跳转页面的时候,可以在 url 后面接 query 参数 然后在 Page 页面的生命周期函数 onLoad 中可以接收到这些参数 这种方式只能 ...

  9. 微信小程序 发现之旅(二)—— 自定义组件

    组件化的项目开发中,组件应当划分为三个层次:组件.模块.页面 微信小程序已经为开发者封装好了基础组件,页面文件(pages)也有了详细的规定 而模块就需要自行开发,并且要和页面文件区分开,这就涉及到自 ...

随机推荐

  1. React可控组件与不可控组件

    一.不可控组件 1.简介 2.代码 <!DOCTYPE html> <html lang="zh-cn"> <head> <meta ch ...

  2. POSIX、XNU

    POSIX 表示可移植操作系统接口(Portable Operating System Interface ,缩写为 POSIX ),POSIX标准定义了操作系统应该为应用程序提供的接口标准,是IEE ...

  3. Android百度地图开发03之地图控制 + 定位

    前两篇关于百度地图的blog写的是,一些基本图层的展示 和 覆盖物的添加+地理编码和反地理编码. 接下来,这篇blog主要说一些关于地图控制方面的内容和定位功能. 百度地图提供的关于地图的操作主要有: ...

  4. JavaScript DOM实战:创建和克隆元素

    DOM来创建和克隆元素. createElement()和createTextNode() createElement()和createTextNode()做的事情正如它们的名字所说的那样.最常见的J ...

  5. HttpClient Post Form提交文件/二进制数据

    HttpClient httpClient = HttpClients.createDefault(); HttpPost httppost = new HttpPost(url); Multipar ...

  6. TestNG超详细教程

    testNG官网:http://testng.org/doc/download.html howtodoinjava.com里的testNG教程,简单详细:http://howtodoinjava.c ...

  7. (六)C#中判断空字符串的三种方法性能分析

    三种方法分别是: string a=""; 1.if(a=="") 2.if(a==string.Empty) 3.if(a.Length==0) 三种方法是等 ...

  8. 两个学生OJ差集

    这个程序非常简单,因为用了最笨的办法,不过运行一点儿也不慢... 在我们学校OJ平台每个人的个人信息中都有Solved Problems List,我们可以用这个简单的程序输入两个人解决问题的所有题号 ...

  9. hdu - 2216 Game III && xtu 1187 Double Maze (两个点的普通bfs)

    http://acm.hdu.edu.cn/showproblem.php?pid=2216 zjt和sara在同一个地图里,zjt要去寻找sara,zjt每移动一步sara就要往相反方向移动,如果他 ...

  10. 【转】Android横竖屏切换问题

    Android横竖屏切换总结(Android资料) Android横竖屏要解决的问题应该就两个: 一.布局问题 二.重新载入问题 1.布局问题:如果不想让软件在横竖屏之间切换,最简单的办法就是在项目的 ...