我有丰富的 C# 经验,但我对 Web 表单还很陌生。
我正在开发一个项目,其中大量数据存储在 ViewState 中,但我不明白为什么。
我的理解是,代表我的页面(System.Web.UI.Page)的对象在该页面的生命周期内是持久的。标识符存储在 ViewState 中可能存在一些幕后魔力,但是当对该页面上的事件做出反应时,我不能简单地引用“this”及其属性/方法吗?
我什么时候会在 ViewState 中显式存储数据,而不是简单地使用当前对象(本例中为 Page)的属性?
我的理解是,代表我的页面(System.Web.UI.Page)的对象在该页面的生命周期内是持久的
这是正确的,但是页面的生命周期仅适用于单个请求,在 HTML 交付给客户端后,
Page
实例将被销毁。
我建议阅读这篇文章:https://msdn.microsoft.com/en-us/library/ms178472.aspx
请注意,WebForms 的设计:将 stateless-web 抽象为类似伪有状态 WinForms 的环境在很大程度上被认为是一个错误,这就是为什么 ASP.NET MVC 和 ASP.NET Core 具有完全不同的设计(尽管
Controller
实例可以与 Page
实例进行比较,具有相似的生命周期语义,但开销要少得多)。
可能有一些幕后魔法,标识符存储在 ViewState 中,但是当对该页面上的事件做出反应时,我不能简单地引用“this”及其属性/方法吗?
我很难理解这一点 - 但如果您认为
Page
的所有实例成员(包括仅使用支持字段的自定义属性)都会在请求之间自动保留(包括“回发”请求),那么不,那不是真的。字段没有神奇的持久性,您需要使用 Page.ViewState
属性作为这些属性的后备存储,并且该数据仅在特殊的“回发”POST 请求之间保留。
让我尝试一下我自己的解释:
GET /MyForm.aspx
MyForm : System.Web.UI.Page
的新实例,创建在 .aspx
文件中声明的所有子控件实例,并调用所有控件上的 Init
和 Load
事件,然后 Render
生成输出HTML,然后 Unload
。然后,请求的 MyForm
实例将被垃圾收集。如果
MyForm.aspx
包含 <form runat="server">
并使用“回发”,那么当用户执行某些触发回发的操作时:
POST /MyForm.aspx
发出请求,其中请求正文是来自 <input >
元素的数据。根据 HTML 规则,仅<input>
(以及<select>
、<textarea>
等)的内容在 POST 请求中提交,而不是整个 DOM - 这对于 ASP.NET WebForms 来说是一个问题,因为所有这些 Controls
具有许多属性和设置(例如 <asp:Label FontColor="">
不通过自己的 <input type="hidden" name="label123_FontColor">
进行持久化,此外,即使它们是,也会发送回服务器的大量详细数据。因此,ASP.NET 指示所有
Control
实例将其所有非 <input>
数据序列化到 ViewState
字典(代表 View 的 state(用 MVP/MVC 的说法, .aspx
/HTML 是视图 - 因此将其状态与请求状态分开,根据定义,请求状态是短暂的)。
在
Control
或 Page
子类中,您需要使用 ViewState,而不是支持字段:
public String SomeName {
get { return this.ViewState["SomeName"] as String; }
set { this.ViewState["SomeName"] = value; }
}
...然后
ViewState
被序列化、压缩、签名并渲染到 <input name="__VIEWSTATE" type="hidden">
中的页面。
之所以在
__VIEWSTATE
中携带状态,而不是在每个页面请求上简单地重新生成整个页面的数据,是因为有时最初生成页面可能会涉及繁重的工作(例如数据库查询)。争论的焦点是,如果您有一个包含数据列表的页面,并且用户在将其保存回数据库之前只是简单地操作该数据,那么您不需要从数据库重新加载,而是将该视图级数据存储在页面的 __VIEWSTATE
就好像它是某种超级 cookie 之类的东西。