优化ASP.NET DataGrid的ViewState大小

ASP.NET开发过程中,ViewState是一个常见的问题,因为它通常占用了大量内存。尽管可以通过禁用特定控件或整个页面的ViewState来减小其大小,但这样可能会导致失去一些高级功能,如选择项目或分页。本文将介绍如何优化DataGrid控件的ViewState,同时保持其所有功能。

当开始学习和实践使用ASP.NET时,ViewState是一个让头疼的问题。通常,ViewState的大小过大,可以通过禁用特定控件或整个页面的ViewState来减小其大小。不幸的是,禁用ViewState后,有时会失去一些功能。对于DataGrid来说尤其如此,禁用ViewState后,将失去选择项目或分页等高级功能。另一方面,DataGrid生成的ViewState通常非常大,并且随着每行每列的添加而增长。

探索过程

在过去的两周里,决定解决这个问题。假设DataGrid在ViewState中存储了DataTable的副本,或者数据是按列存储的BoundColumn对象。但后来发现这是一个错误的假设。

首先,展示了用于显示页面的整个控件树。第一个惊喜是DataGrid内部使用了其他控件来显示其内容。下面是一个2x2 DataGrid的示例控件树:

DataGrid DataGridTable DataGridItem TableCell TableCell DataGridItem TableCell TableCell

DataGridTable类是从System.Web.UI.WebControls.Table派生的。DataGridItem对象表示行,TableCell表示DataGrid的单元格。

接下来,使用反射工具(Reflector)反编译.NET二进制文件,并浏览了DataGrid、BaseDataList(其基类)、BoundColumn和DataGridColumn类的代码。不幸的是,没有找到可以帮助解决问题的内容。

在这一点上,决定尝试解码并分析ViewState的内容。使用了ViewState Decoder工具来完成这项工作。当显示ViewState的数据时,其结构对来说很熟悉。之后,发现它类似于页面上的控件树。想,如果禁用DataGrid每行的ViewState会怎样?检查了这个方法,结果发现...就是它!

以下是减小DataGridViewState大小的一些方法:

  • 禁用ViewState:如果可能,禁用整个DataGrid的ViewState(将EnableViewState属性设置为false),或者更好的是禁用整个页面。如果需要DataGrid的高级功能,如选择项目或分页,那么不能使用这种方法。在这种情况下,应该使用下面列出的方法。
  • 禁用列自动生成:将AutoGenerateColumns属性设置为false,并使用Property Builder手动创建DataGrid控件的列。当列自动生成时,有关它们的信息会存储在ViewState中。
  • 禁用DataGrid每行的ViewState:这是最好的方法,因为显示的数据副本存储在ViewState中,作为TableCell.Text属性的值。禁用后,DataGrid ViewState的大小将保持不变,即使显示更多行也不会增加。可以使用以下代码实现这一点:
C# private void DisableViewState(DataGrid dg) { foreach (DataGridItem dgi in dg.Items) { dgi.EnableViewState = false; } } private void Page_Load(object sender, System.EventArgs e) { MyDataGrid.DataSource = GetData(); MyDataGrid.DataBind(); DisableViewState(MyDataGrid); }
  • 将所需的标识符存储在ViewState中作为string[]数组:当显示数据给用户时,需要存储显示数据的标识符。可以将它们存储在隐藏的DataGrid列中,但这并不是最佳解决方案,因为ID将作为DataGridItem/TableCell/string树存储(还需要更改前面的代码,以仅禁用选定列的ViewState)。更好的解决方案是将ID存储在ViewState中作为string[]数组。注意:int[]数组可能看起来更合适,但不是:由LosFormatter类生成的数据对于string[]数组比对于int[]数组更紧凑。这是因为LosFormatter类仅针对string[]数组进行了优化

例如:有一个包含三个ID的数组:1、2、3。如果将它们存储在ViewState中作为string[],将得到:

@<1;2;3;>

如果将它们存储为int[],将得到:

@System.Int32, mscorlib, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;i<2>;i<3>;>

如果在ViewState中存储更多的int[]数组,除了第一个之外的所有数组都将以以下方式存储:

@50;i<2>;i<3>;>

或者,可以将ID存储在ArrayList中。以下是从ArrayList生成的数据,存储为string和int:

l<1;2;3;> l;i<2>;i<3>;>

以下是创建并存储ID数组在ViewState中的示例代码:

C# DataTable dt = GetData(); MyDataGrid.DataSource = dt; MyDataGrid.DataBind(); string[] ids = new string[dt.Rows.Count]; for (int n = 0; n < dt.Rows.Count; ++n) ids[n] = dt.Rows[n]["id"].ToString(); ViewState["ids"] = ids;

当需要检索显示数据的ID时,请使用以下代码:

C# int index = MyDataGrid.SelectedIndex; string[] ids = (string[])ViewState["ids"]; int myID = Convert.ToInt32(ids[index]);

统计数据

创建了一个页面,显示了一个10x10的表格。每个单元格包含一个三位数。以下是测试结果:

ViewState大小 描述
6032 VS启用,CA启用
5028 VS启用,CA禁用
236 VS为行禁用,CA禁用
948 VS为所有列的单元格禁用(除了第一列),CA禁用
476 VS为行禁用,CA禁用,ID以int[]存储在VS中
316 VS为行禁用,CA禁用,ID以string[]存储在VS中
356 VS为行禁用,CA禁用,ID以int存储在VS中的ArrayList中
316 VS为行禁用,CA禁用,ID以string存储在VS中的ArrayList中

如所见,ViewState的大小从6032字节减少到316字节。节省了5716字节,现在ViewState仅包含初始数据的5.2%!

要点

花了将近两周的时间来研究本文中介绍的主要方法(禁用DataGrid每行的ViewState)。确实,最简单的解决方案往往是最难找到的。

沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485