控件状态
我先前提过,ASP.NET 1.x 中使用服务器端控件最另人沮丧的一面是关于 ViewState 的非全有即全无的状态。控件的行为方面,如 DataGrid 中的分页或文本框中的选定更改通知,需要启用 ViewState 来正常运行。我确定在任何 DataGrid 中启用 ViewState 的前景令所有人十分沮丧。在 ASP.NET 2.0 中,Microsoft 通过将 ViewState 分割成两个独立不同的类别解决了这一特殊问题: ViewState 和控件状态。
控件状态是另一类隐藏的状态,专门为维护控件的核心行为功能而保留,而 ViewState 只包含维护控件内容 (UI) 的状态。从技术上来说,控件状态存储在与 ViewState 相同的隐藏字段中(只是在 ViewState 层次结构末端的另一个叶子节点),但是如果禁用一个特定控件或整个页面的 ViewState ,控件状态仍然传播。实现这一技术好的一面是,现在我们能够改进 ASP.NET 2.0 优化 ViewState 的原始原则,使之更强健:如果在每次请求页面时传播控件内容,您应该禁用该控件的 ViewState 。
只要控件生成器将其状态正确划分成行为状态(为了维护核心功能)和 UI 状态(为了保留内容),就应该坚定地遵守这个原则。我建议您现在使用 ASP.NET 2.0 时开始遵循这个原则,但是 DataGrid 还未重构以保留控件状态中的行为状态。幸运的是,新的 GridView 是 DataGrid 的逻辑继承者,它像 TextBox 和 DropDownList 控件的新版本一样正确使用控件状态。图 5 显示的是目前使用控件状态的控件列表及其存储的属性,供您参考。随着 ASP.NET 2.0 的最终发布,这个列表可能会作出变动。
如果您愿意进一步研究 ASP.NET 2.0 的新控件如何使用控件状态和 ViewState ,可以使用我编写的一个叫做 View State Decoder 的实用工具,它用来显示给定页面上所有的 ViewState 和控件状态。图 6 显示的是活动的实用工具的一个屏幕快照。您能够从本文顶端的链接下载该程序。注意,这个应用程序依赖于目前 ViewState 使用的编码字符,在最终版本发布前可能会改变。
对于您的那些生成控件,控件状态的使用模型不像 ViewState 一样方便。您必须重写 LoadControlState 和 SaveControlState 虚方法,手动管理您那部分映射到控件状态中的对象集合,而非提供一个带有索引的状态包用来插入和移除选项。另一件您必须做的事情是在初始化期间调用 Page.RegisterRequiresControlState 方法,使页面能够在正确的时间询问控件状态。在控件状态中存储选项比在 ViewState 中更难可能是一件好事,因为用户无法禁用它(控件状态有一个选择模型也有性能方面的好处)。开发人员在存储任何状态前应该认真考虑,因为它总是添加到呈现的页面中。图 7 显示在控件状态中存储一个字符串的自定义控件(本例中是它的颜色)。
当您决定控件状态和 ViewState 各存储什么内容时,请记住行为状态与内容或者 UI 状态应该有分别。诸如属性和数据集一样的内容一般应存储在 ViewState 中,而不是迁移到控件状态。触发服务器端事件的状态是存储在控件状态中最典型的一类状态。
声明性数据源和 ViewState
应用我们的 ViewState 优化原则有一个小问题 — 您经常不知道每次请求时是否正在填充控件内容。在 ASP.NET 2.0 中采用声明性数据源意味着将数据绑定到一个控件不再需要将数据源属性显式连接至 DataReader 或 DataSet,并调用 DataBind。而是在页面上声明性地放置一个数据源,并将控件指向该数据源。例如,使用绑定到 pubs 数据库中 authors 表格的新 GridView 类和与之相关的 SqlDataSource(参见 图 8)。如果运行这个页面,您将发现它正好有效。GridView 及其相应的数据源能够了解何时进行交互。在页面呈现之前,GridView 充满数据源的数据,并且很可能在客户端呈现整个 authors 表格。
在这个简单的情况中,我们还未禁用 GridView 的 ViewState ,因此您可能会认为,我们再一次仅仅使用 ViewState 存储了从未使用的数据。幸运的是,ASP.NET 2.0 引擎作了正确的事情,当控件上的 ViewState 可用时,它将不厌其烦地返回到数据库。
同样地,如果禁用 GridView 上的 ViewState ,数据绑定到数据源将发生在每次请求发生的时候,包括 POST back 请求。DataBoundControl 基类中置入了这一功能,其中的 AdRotator、BulletedList、CheckBoxList、DropDownList、ListBox、 RadioButtonList、GridView、DetailsView 和 FormView 控件继承了该功能。这些控件展示了 ViewState 在绑定到声明性数据源时表现的智能使用。
小结
ASP.NET 的下一个版本对 Web 开发人员承诺了许多改进,不只是更有可能晚上好好休息,不会做 Base64 编码数据淹没了 Web 窗体的噩梦。使用更紧凑的序列化格式、行为状态和 UI 状态的分离以及数据绑定控件和声明性数据源的智能交互,那些 ViewState 的噩梦仅可能变成美梦。
Fritz Onion 是 Pluralsight — 教育与内容创作公司 — 的共同创始人之一,他致力于使用 ASP.NET 进行 Web 开发。Fritz 是 Essential ASP.NET (Addison Wesley, 2003) 一书的作者。阅读他的网络日记,请访问 http://www.pluralsight.com/fritz。
