当然, viewstate 在 asp.net 中有个重要的角色。如果使用恰当,它能够简化页面开发,改进用户与站点的交互。如果置之不理,它能够显著增加站点响应大小,在连接速度慢的情况下,使您的响应时间更加缓慢。asp.net 2.0 的发布带来了 viewstate 机制的一些改进,这使得 viewstate 使用更简单,又不会防碍站点性能。这些改进包括:减少编码数量,采用控件状态从内容中分离出行为状态,以及智能集成数据绑定控件。
viewstate 基本原理
在介绍 asp.net 2.0 viewstate 的改进之前,简要总结目前版本中 viewstate 的用途和实现是适宜的。 viewstate 为 asp.net 开发人员解决了一个特定问题 — 保留服务器端不形成元素的控件的状态。这很重要,因为 asp.net 中的大部分服务器端控件模型是根据这样一个假设生成的,那就是 — 如果用户回发到相同页面,所有控件保持其状态不变。也就是说,如果在处理请求期间修改任何控件的内容,任何后续 post 请求回到相同页面时,您可以依赖于那些仍然存在的修改。作为一个活动的 viewstate 示例,尝试运行下面显示的代码。
<%@ page language="c#" %>
<script runat="server">
protected override void onload(eventargs e)
{
int val = int.parse(_sum.innertext);
_sum.innertext = (val+1).tostring();
base.onload(e);
}
</script>
<html>
<body>
<form runat="server">
<h2>viewstate test page</h2>
<span id="_sum" runat="server">0</span>
<br />
<input type="submit" />
</form>
</body>
</html>
每次按下 submit 按钮时,_sum 范围值递增。因为 viewstate 在请求期间保持以前的值,因此它将从上一次显示的值开始,显示 1、2、3、4 等等。如果想知道 viewstate 不可用时发生的事情,尝试添加 enableviewstate='false' 作为范围元素的一个属性。因为以前的范围值在请求处理时没有传播,所以不论页面发布多少次,都将显示值为 1。
在 asp.net 中, viewstate 完成基于控件的编程模型。如果没有 viewstate ,一些控件(如文本框和下拉列表)在 post 请求期间保持状态,而其他控件不保持,使用这些状态各异的控件记录一些特殊的情况是令人沮丧的体验。使用 viewstate ,开发人员能够专注于编程模型和用户界面,而不用担心状态保持。还能对 viewstate 进行哈希或加密,以防止用户篡改或解码。
使用 viewstate 的另一个重要之处是在控件中发布服务器端更改事件。如果用户改变了文本框中的值或切换了下拉列表中的选定元素,您就能够注册一个事件处理程序,引发事件时,执行代码。这些控件比较其当前值与以前值,如果有任何过程预订更改事件,以前值隐式存储在 viewstate 中。如果禁用一个控件的 viewstate ,而您正在处理该控件的更改通知事件,因为总是假定以前值与窗体默认值相同,所以更改通知事件不会正确激发。
viewstate 问题
正如我早前指出的,在 asp.net 1.x 中, viewstate 有很多问题。默认情况下,它是启用的,除非您知道在不需要使用时找到并禁用它,否则它能显著增加页面呈现的数据量。当使用数据绑定控件时,所有控件都使用 viewstate 保存回发后的状态,这将变得异常痛苦。作为一个简单而生动的示例,asp.net 页面代码:
<%@ page language="c#" %>
<%@ import namespace="system.data" %>
<%@ import namespace="system.configuration" %>
<%@ import namespace="system.data.sqlclient" %>
<script language="c#" runat="server">
protected override void onload(eventargs e)
{
string dsn = configurationsettings.appsettings["dsnpubs"];
string sql = "select * from authors";
using (sqlconnection conn = new sqlconnection(dsn))
using (sqlcommand cmd = new sqlcommand(sql, conn))
{
conn.open();
_dg1.datasource = cmd.executereader();
_dg1.databind();
}
base.onload(e);
}
</script>
<html>
<body>
<form id="form1" runat="server">
<asp:datagrid id="_dg1" runat="server" />
</form>
</body>
</html>
这个页面有一个 datagrid 控件,该控件绑定对 pubs 数据库中 authors 表格进行简单查询的结果。如果运行这个页面(对连接字符串做出必要改正),查看 viewstate 字段,您可能吃惊地发现里面有超过 12,000 个字符。当查看页面内容,意识到显示浏览器中整个表格内容所需的实际字符数量大约是 1,600,您会更加吃惊。造成这个结果的原因之一是 viewstate 不仅编码数据,而且编码数据类型(元数据);同样,base64 编码一般要增加大约 33 % 的空间系统开销。而且,大量的系统开销只是为了保留 post 请求后的控件状态,这似乎与时间不成比例,必须竭尽全力避免发生。
很明显,如果您关心响应大小,那么决定何时禁用控件的 viewstate 是重要的。刚才的示例就是一个典型的情况,传播了 viewstate ,但是从未使用,这是优化站点的 viewstate 使用时建议采用的主要规则:如果每次请求页面时填充控件内容,禁用该控件的 viewstate 一般是安全(而明智)的。
另一方面,您可能决定利用 viewstate 保留控件状态这一实事,并且只在页面的首次 get 请求时,填充控件内容(只是贯穿后续 post 请求)。这省去了使用的任何后端数据源的往返行程。通过修改我的页面来利用这一结论,我更改了 onload 方法,使它在填充 datagrid 前检查 ispostback 标志,如以下代码所示。
protected override void onload(eventargs e)
{
if (!ispostback)
{
string dsn = configurationsettings.appsettings["dsnpubs"];
string sql = "select * from authors";
using (sqlconnection conn = new sqlconnection(dsn))
using (sqlcommand cmd = new sqlcommand(sql, conn))
{
conn.open();
_dg1.datasource = cmd.executereader();
_dg1.databind();
}
}
base.onload(e);
}
当然,有一些其他可能更好的选择,例如缓存服务器的查询结果,每次发出请求时重新绑定控件。由您权衡状态存储的位置和特定体系结构与应用程序的重要性。
您可能注意到我曾小心提过,如果每次请求页面时填充控件内容,那么禁用 viewstate “一般”是安全的。例外情况是,一些控件既使用 viewstate 保留行为,也保留一般状态。如前所述,下拉列表和文本框控件使用 viewstate 存储以前的值来正确发布服务器上的更改通知事件。同样地,datagrid 类使用 viewstate 发布分页、编辑和排序事件。遗憾的是,如果您想要使用 datagrid 中诸如排序、分页或编辑的功能,则不能禁用其 viewstate 。对于尝试生成快速有效站点的开发人员来说,服务器端控件 viewstate 的非全有即全无的状况,是 asp.net 1.x 的服务器端控件模型另人沮丧的一面。
新闻热点
疑难解答
图片精选