DataGrid的ViewState优化






4.59/5 (25投票s)
2005年4月11日
5分钟阅读

169828

1015
如何减小 DataGrid 的 ViewState 大小,同时保持 DataGrid 的所有功能。
引言
本文演示了如何减小 DataGrid
控件生成的 ViewState 数据的大小,同时保持 DataGrid 的所有功能,例如选择项目或分页。
我假设您知道 ViewState 是什么以及如何使用它。如果不知道,请先学习相关知识,例如阅读文章 《ASP.NET ViewState 的一点小技巧》。在该文章中,您还可以找到关于减小 ViewState 大小的通用信息。
背景
当我开始在实践中学习和使用 ASP.NET 时,一个问题是 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 呢?我测试了一下… 成功了!就是这样!
如何减小 DataGrid 的 ViewState 大小 - 总结
禁用 ViewState
如果可以,请禁用整个 DataGrid
的 ViewState(将属性 EnableViewState
设置为 false
),或者更好地禁用整个页面的 ViewState。如果您需要 DataGrid
的高级功能,例如选择项目或分页,则不能使用此方法。在这种情况下,您应该使用以下列出的方法。
禁用列的自动生成
将属性 AutoGenerateColumns
设置为 false
,并使用 DataGrid
控件的属性生成器手动创建列。当列自动生成时,有关它们的信息会存储在 ViewState 中。
禁用 DataGrid 每行的 ViewState
这是最好的方法,因为显示数据的副本以 TableCell.Text
属性的值的形式存储在 ViewState 中。当您禁用此项后,DataGrid
的 ViewState 大小将保持不变,并且在显示更多行时不会增加。您可以使用以下代码来实现此目的:
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 作为 string[]
数组存储在 ViewState 中。注意:int[]
数组可能看起来更合适,但并非如此:由 LosFormatter
类生成的数据对于 string[]
数组比对于 int[]
数组更紧凑。这是因为 LosFormatter
类仅针对 string[]
数组进行了优化。
示例:您有一个包含三个 id 的数组:1、2、3。如果您将它们作为 string[]
存储在 ViewState 中,您将得到:
@<1;2;3;>
当您将它们作为 int[]
存储时,您将得到:
@System.Int32, mscorlib, Version=1.0.5000.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089<i<1>;i<2>;i<3>;>
如果您将更多 int[]
数组存储在 ViewState 中,除了第一个之外,所有其他的都将按照以下方式存储:
@50<i<1>;i<2>;i<3>;>
或者,您可以将 id 存储在 ArrayList
中 - 下面是存储为 string
和存储为 int
的值从 ArrayList
生成的数据:
l<1;2;3;>
l<i<1>;i<2>;i<3>;>
以下是一个创建并将在 ViewState 中存储数组的示例代码:
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 时,请使用以下代码:
int index = MyDataGrid.SelectedIndex;
string[] ids = (string[])ViewState["ids"];
int myID = Convert.ToInt32(ids[index]);
统计
我创建了一个显示 10x10 表格的页面。每个单元格都包含一个三位数字。以下是我的测试结果:
VS - ViewState,CA - 列自动生成。
描述 | ViewState 大小 |
---|---|
VS 已启用,CA 已启用 | 6032 |
VS 已启用,CA 已禁用 | 5028 |
VS 对行禁用,CA 已禁用 | 236 |
VS 对除第一列外的所有列的单元格禁用,CA 已禁用 | 948 |
VS 对行禁用,CA 已禁用,id 以 int[] 形式存储在 VS 中 |
476 |
VS 对行禁用,CA 已禁用,id 以 string[] 形式存储在 VS 中 |
316 |
VS 对行禁用,CA 已禁用,id 以 ArrayList 中的 int 形式存储 |
356 |
VS 对行禁用,CA 已禁用,id 以 ArrayList 中的 string 形式存储 |
316 |
您可以看到,ViewState 的大小从 6032 减少到了 316 字节。我们节省了 5716 字节,现在 ViewState 仅包含初始数据的 5.2%!
关注点
我花了将近两周时间研究本文介绍的主要方法(禁用 DataGrid 每行的 ViewState)。最简单的解决方案往往是最难找到的。
我还发现,存储在 ViewState 中的数据可以进行优化。如果您知道 LosFormatter
的工作原理,就可以做到这一点。另一种方法是实现自己的类来替换 LosFormatter
,这将在另一篇文章中讨论。
历史
- 4/11/2005
First version.