ASP.NET中的数据绑定:哪个更快?

写这个随笔的起因是JGTM'2003.NET Blog的 这个同名随笔 http://blog.joycode.com/jgtm2000/posts/9089.aspx,该文中例举了5种asp.net数据绑定的写法
<%# DataBinder.Eval(Container.DataItem, "ColumnName") %>
<%# DataBinder.Eval(Container.DataItem, "ColumnName", null) %>
<%# DataBinder.Eval(Container, "DataItem.ColumnName", null) %>
<%# ((DataRowView)Container.DataItem)["ColumnName"] %>
<%# ((DataRowView)Container.DataItem).Row["ColumnName"] %>
由于对其结论:
实际上根据我们的测试,第4种写法的效率甚至比不上最普遍的第1种写法,也就是说这样进行优化适得其反!

颇为怀疑,所以做了一个粗粗的测试,再后来又做了一个详细的测试。

重要说明

repeater1.DataSource=ds.Tables[0].DefaultView; 和
repeater1.DataSource=ds.Tables[0];

两者有区别吗?dino esposito都犯了错误
构建web解决方案—应用asp.net 和ado.net 中,第12页和第13页中写了,如果DataSource是DataTable,则数据绑定语法写作
<%# ((DataRow)Container.DataItem)["field"] %>
当然,在实际中这是行不通的
如果我们了解asp.net 服务器控件数据绑定过程和DataTable的实现,就会理解为什么是上面的看法是错误的
对于实现IListSource接口的对象,像DataTable,服务器控件总是会读取其IListSource的GetList返回的数据作为数据源
DataTable的GetList实现是
  IList IListSource.GetList()
    {
      return DefaultView;
    }
因此,上面两行代码其实是一样的
所有,只有写成
<%# ((DataRowView)Container.DataItem)["field"] %>
编译才能通过

测试方法


这里说一下我的简单测试方法,大家可以看看这个测试方法是否有问题。对于机器和具体数字大家不必关系, 因为我们主要是看对比数据。由于机器上装的是.net framework 1.2,而且1.2又引入了Eval,所以我把Eval(ColumName)这种写法作为第六种测试

测试页面Default2.aspx
定义一个私有变量
    
private DateTime startDate;
在Page_Load中,我们读取记录并进行数据绑定
  void Page_Load(object sender, System.EventArgs e)
    {
       
        SqlConnection con = new SqlConnection ("server=(local);user id=sa;pwd=;database=repair");
        con.Open ();
        SqlDataAdapter sda = new SqlDataAdapter ("select name from m_item", con);
        DataSet ds = new DataSet ("item");
        sda.Fill (ds, "item");
        repeater1.DataSource = ds.Tables[0].DefaultView;
        startDate = DateTime.Now;
        repeater1.DataBind ();
        sda.Dispose ();
        con.Close ();
        con.Dispose ();
    }

在Page_LoadComplete时进行记录
 void Page_LoadComplete(object sender, System.EventArgs e)
    {
       
        TimeSpan span=DateTime.Now.Subtract(startDate);
       
        double speed=span.TotalMilliseconds;
        SqlConnection con = new SqlConnection ("server=(local);user id=sa;pwd=;database=recording");
        con.Open();
        SqlCommand cmd = con.CreateCommand ();
        cmd.CommandText = "insert into databinding (typeid,speed) values (@typeid,@speed)";
        cmd.CommandType = CommandType.Text;
 //@typeid的值根据每次测试不同改变
        cmd.Parameters.AddWithValue ("@typeid", 6);
        cmd.Parameters.AddWithValue("@speed",speed);
        cmd.ExecuteNonQuery ();
        cmd.Dispose ();
        con.Close ();
        con.Dispose ();
      
       
    }

   
测试页面由default3.aspx完成
   void Page_Load (object sender, EventArgs e)
    {
        for (int i = 1; i <= 100; i++)
        {
            Server.Execute ("default2.aspx");
        }
    }

这里我们对每种方法执行100次   
绑定是通过一个Repeater控件来进行的
    <asp:repeater id="repeater1" runat=server>
    <itemtemplate>
<%# Eval("name") %>
    </itemtemplate>
    </asp:repeater>

其中Itemtemplate部分每次使用不同的方法

对测试后的数据我们通过查询进行分析
SELECT     Typeid, AVG(Speed) AS Expr1
FROM         databinding
GROUP BY Typeid
ORDER BY expr1

结果(单位:毫秒)
 4 182.03125
 5 182.65625
 1 216.09375
 6 221.5625
 2 226.5625
 3 302.34375

关于4和5 区别


因为4实际上调用的是
1、DataRow.Item(property, !dataView.IsOriginalVersion(index) ? DataRowVersion.Default : DataRowVersion.Original);

2、DataRow.Item(DataColumn,DataRowVersion)

而5首先调用过程是

1、DataRow.Item(string)
2、DataRow.Item(DataColumn)
3、DataRow.Item(DataColumn,DataRowVersion)

所以有一些细微的差别

说明:关于asp.net 2.0中的Eval实现

Eval在Page类中实现,其本质如下

    protected object Eval(string expression)
    {
      return DataBinder.Eval(GetDataItem(), expression);
    }

    protected object Eval(string expression, string format)
    {
      return DataBinder.Eval(GetDataItem(), expression, format);
    }

所以大家对它的效率应该能够估计到

补充

JGTM'2003发布了他的测试代码
我以为DataBind应该在asp.net环境下测试,并且应该调用DataBind方法,否则就变成一些方法调用性能测试了。

另外关于4和1的差别,我测试了32个字段的结果,还是是4远快于1

至于两者的差别,我还是说不出所以然来

关于DataBinder.Eval的内部实现,实际上类似如下代码

PropertyDescriptor propertyDescriptor = TypeDescriptor.GetProperties(Container.DataItem).Find(columName, true);
return propertyDescriptor.GetValue(Container.DataItem);

这里我同意JGTM'2003的说法,虽然这是通过看来是通过反射调用,但实际上对于实现ICustomTypeDescriptor接口的对象, 调用速度还是不能用平常眼光来看。DataRowView实现了ICustomTypeDescriptor方法,调用的是其对应Datatable的 GetPropertyDescriptorCollection方法,正如JGTM'2003所说,DataTable有 UpdatePropertyDescriptorCollectionCache方法,在第一次获取时组装数组

但即使如此,TypeDescriptor的调用并不能说是超过了对DataColumnCollection的检索
我构造了一个1000列和1000行的datatable

DataTable t = TestDataProvider.GetDataTable (1000, 1000);
DataView dv = new DataView (t);
DataRowView drv=dv[0];
DateTime startTime=DateTime.Now;

for (int i = 0; i < 10000; i++)
{
PropertyDescriptor pd = TypeDescriptor.GetProperties (drv).Find ("column1",true);

pd.GetValue (drv).ToString();
}
TimeSpan span=DateTime.Now.Subtract(startTime);
Response.Write ("<br/>" + span.TotalMilliseconds.ToString ());
startTime=DateTime.Now;

for (int i = 0; i < 10000; i++)
{
drv.Row["column739"].ToString();
}
span=DateTime.Now.Subtract(startTime);
Response.Write ("<br/>" + span.TotalMilliseconds.ToString ());

大家可以对比结果,DataColumnCollection的检索还是快了许多倍

结论

JGTM'2003的进一步测试终于找出了我们的分歧点在何处,即DataColumnCollection的Item(name)方法在列名大小 写全匹配时和不匹配时的处理不同。当大小写不匹配时,还会调用IndexOfCaseInsensitive(name)方法以获取正确的 DataColumn

我的测试总是字段名总是大小写匹配的,所以导致了一些细节差异。但我们还是可以总结出一些结论来

1、第4,5种在使用columnIndex中无疑是最快的
2、第4,5种在使用列名大小写匹配时无疑也是最快的
3、当4,5种在使用列名大小写不匹配时,同第一种相比结果很微妙,可以看JGTM'2003的测试结果
4、 但是结论3 这种比较是基于DataRowView对DataBinder.Eval调用的优化(即实现了ICustomTypeDescriptor) 接口而言的,对于没有实现该接口的数据集合绑定,如一个自定义的对象集合,无疑,4,5两种的调用的模式还是推荐之列的

我认为
原则上大家应该使用第4,5种,在大量字段时考虑字段名的匹配问题,如果是自己创建的商业组件,在一定时候可以考虑实现ICustomTypeDescriptor接口来对DataBinder.Eval进行一些优化

 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值