ASP.NET 状态管理新手教程






4.77/5 (73投票s)
ASP.NET 状态管理新手教程
引言
本文探讨了 ASP.NET 应用程序中状态管理的必要性、ASP.NET 中状态管理的各种方法以及所有状态管理技术的比较分析。
背景
HTTP 是一种无状态协议。一旦服务器处理完用户的任何请求,它就会清除用于处理该请求的所有资源。这些资源包括在该请求期间创建的对象、在该请求期间分配的内存等。对于来自 Windows 应用程序开发背景的人来说,这可能是一个巨大的惊喜,因为他无法仅仅依靠对象和成员变量来跟踪应用程序的当前状态。
如果我们需要在页面访问之间甚至在同一页面的多次访问之间跟踪用户信息,那么我们需要使用 ASP.NET 提供的状态管理技术。状态管理是 ASP.NET 允许开发人员在同一或不同页面的多个请求中维护状态和页面信息的过程。
状态管理的类型
ASP.NET 主要提供两种类型的状态管理
- 客户端状态管理
- 服务器端状态管理
当我们使用客户端状态管理时,状态相关信息将存储在客户端。这些信息将随每个请求和响应来回传输。这可以形象化为

注意:图片摘自 Microsoft press 的书籍。
这种状态管理的主要好处是,我们减轻了服务器保留状态相关信息的负担,节省了大量的服务器内存。客户端状态管理的缺点是它占用了更多的带宽,因为大量数据来回传输。但还有一个比带宽使用问题更大的问题。客户端状态管理使信息来回传输,因此这些信息可能会被中间的任何人截获。所以我们无法在客户端存储密码、信用卡号和应付金额等敏感信息,对于此类信息我们需要服务器端状态管理。
与客户端状态管理相反,服务器端状态管理将所有信息保存在用户内存中。其缺点是服务器内存使用量增加,优点是用户的机密和敏感信息是安全的。

注意:图片摘自 Microsoft press 的书籍。
我们不能说在应用程序中只使用一种状态管理类型。我们需要根据信息的类型和大小,在客户端和服务器端状态管理之间找到一个折衷方案。现在让我们看看如何在客户端和服务器端管理状态的不同方法。
客户端状态管理技术
- 视图状态
- 控制状态
- 隐藏字段
- Cookie
- 查询字符串
服务器端状态管理技术
- 应用程序状态
- 会话状态
视图状态
ASP.NET 使用此机制来跟踪同一页面的页面请求之间网页上控件的值。我们还可以向视图状态添加自定义值。ASP.NET 框架负责将控件信息存储在视图状态中,并在回发时呈现之前从视图状态中检索它。
如果我们需要使用 viewstate
来存储我们的信息,我们只需要记住 viewstate
是一个 dictionary
对象。我们可以将数据以键值对的形式存储在 viewstate
中(请参见下面的代码)。控件信息也会在请求期间哈希到此字典中,并在响应期间重新填充。
由于此信息存储在网页本身中,ASP.NET 会对信息进行加密。我们可以通过 web.config 调整加密相关参数。
<Configuration>
<system.web>
<pages viewStateEncryptionMode="Always"/>
</system.web>
</configuration>
或页面声明
<%@ Page Language="C#" AutoEventWireup="true"
CodeFile="Default.aspx.cs" Inherits="_Default" ViewStateEncryptionMode="Always"%>
现在让我们看一个 viewstate
的小实现。我们有一个带有 textbox
和 button
的简单网页。我们的想法是,我们将在文本框中输入一些内容,并查看 ASP.NET 如何将此信息存储在视图状态中。我们也将自己的信息存储在视图状态中。当我们运行页面并在 textbox
中输入我的名字并按下按钮时,会发生回发,但我的名字仍然保留在 textbox
中。Viewstate
使之成为可能,因此在回发之后,页面看起来像

当我们查看源代码时,视图状态看起来像
<input type="hidden" name="__VIEWSTATE"
id="__VIEWSTATE" value="/wEPDwUKMTkwNjc4NTIwMWRkfIZa4Yq8wUbdaypyAjKouH5Vn1Y=" />
现在让我们尝试在 viewstate
中添加我们自己的信息。让我们跟踪用户在此页面上的回发次数。每当用户点击一个按钮时,我们都会将存储的回发值加 1。实现方式如下
protected void Page_Load(object sender, EventArgs e)
{
if(IsPostBack == true)
{
if (ViewState["number"] != null) //Lets retrieve, increase and store again
{
ViewState["number"] = Convert.ToInt32(ViewState["number"]) + 1;
}
else //First postback, lets store the info
{
ViewState["number"] = 1;
}
Label1.Text = ViewState["number"].ToString();
}
}
当我们运行页面并点击按钮进行回发时,网页将向我们显示目前已完成的回发次数,这些回发次数存储在 viewstate
中

视图状态默认启用,但我们可以通过将每个 Web 控件的 EnableViewState
属性设置为 false
来禁用它。这会减少服务器处理时间并减小页面大小。
控制状态
我们现在知道 viewstate
是什么,我们也知道我们可以为页面上的控件禁用 viewstate
。但想象一下,如果我们正在开发一个自定义控件,并且我们在内部使用 viewstate
来存储一些信息,但该控件的用户可以禁用我们控件的 viewstate
。为了避免这个问题,我们可以拥有 viewstate
类似的行为,但不能被控件用户禁用,它被称为 ControlState
。控件状态位于自定义控件内部,其工作方式与 viewstate
相同。
要在自定义控件中使用控件状态,我们必须重写 OnInit
方法并在初始化期间调用 RegisterRequiresControlState
方法。然后我们必须重写 SaveControlState
和 LoadControlState
方法。
隐藏字段
隐藏字段是 ASP.NET 提供的控件,它们允许我们将一些信息存储在其中。隐藏字段的唯一限制是它将在进行 HTTP POST 时(即按钮点击时)保留信息。它不适用于 HTTP GET
。现在让我们使用 HiddenFields
来完成跟踪回发的相同练习。
(注意:ViewState
在底层也使用隐藏字段。)
//Store in Hidden Field -----------------------------------------------------------
int newVal = Convert.ToInt32(HiddenField1.Value) + 1; //Hidden field default value was 0
HiddenField1.Value = newVal.ToString();
Label2.Text = HiddenField1.Value;
当我们运行页面并点击按钮进行回发时,网页将向我们显示目前已完成的回发次数,这些回发次数存储在 Hiddenfields
中(详细信息请参见代码)。
Cookie
在某些情况下,我们需要在页面请求之间存储数据。到目前为止,我们讨论的技术都是针对单个页面请求存储数据。现在我们来看看在页面请求之间存储信息的技术。
Cookies 是可以存储在用户计算机上文本文件中的小段信息。服务器可以访问这些信息,并可用于存储在页面访问之间以及用户多次访问同一页面之间所需的信息。现在让我们使用 Cookies 来完成跟踪回发的相同练习。
int postbacks = 0;
if (Request.Cookies["number"] != null) //Lets retrieve, increase and store again
{
postbacks = Convert.ToInt32(Request.Cookies["number"].Value) + 1;
}
else //First postback, lets store the info
{
postbacks = 1;
}
Response.Cookies["number"].Value = postbacks.ToString();
Label3.Text = Response.Cookies["number"].Value;
我们无法使用 cookie 跟踪回发,因为 cookie 会保留在用户机器上,所以我们实际上是在查看用户自开始以来在该页面上回发的次数。
当我们运行页面并点击按钮进行回发时,网页将向我们显示目前已完成的回发次数,这些回发次数存储在 Cookies 中(详细信息请参见代码)。Cookies 可以有各种参数,例如它们的有效期和何时过期。这些参数可以如下操作
Response.Cookies["number"].Expires = DateTime.Now.AddDays(1);
此 cookie 将在其创建后 1 天过期。
查询字符串
查询字符串通常用于存储标识特定页面(例如搜索词或页码)的变量。查询字符串是附加到页面 URL 末尾的信息。它们可用于在页面之间甚至在同一页面之间存储/传递信息。现在让我们来处理在查询字符串中存储回发信息
//GetDataItem from querystring
if (Request.QueryString["number"] != null) //Lets retrieve, increase and store again
{
Label4.Text = Request.QueryString["number"];
}
//set in query string
int postbacks = 0;
if (Request.QueryString["number"] != null) //Lets retrieve, increase and store again
{
postbacks = Convert.ToInt32(Request.QueryString["number"]) + 1;
}
else //First postback, lets store the info
{
postbacks = 1;
}
Response.Redirect("default.aspx?number=" + postbacks);
这里要注意的一点是,我们无法将回发信息存储在处理同一页面的查询字符串中。原因是查询字符串每次都会创建一个新的 URL,每次使用查询字符串时都会是一个新的请求。所以我们现在实质上是在跟踪点击次数。查询字符串背后的思想是向其他页面传递少量信息,这些信息可以用于在该页面上填充信息。

注意:此处使用 cookie 和查询字符串仅用于演示目的。在实际场景中,它们绝不应用于存储同一页面所需的信息。查询字符串应用于在多次页面访问之间存储信息。Cookie 应用于在同一台计算机多次访问我们的网站之间存储信息。
应用程序状态
ASP.NET 允许我们使用应用程序状态保存值。一个全局存储机制,可从 Web 应用程序中的所有页面访问。应用程序状态存储在应用程序键/值字典中。此信息也将对网站的所有用户可用。如果我们需要特定于用户的信息,那么我们最好使用 sessionstate
。
ASP.NET 提供了三个事件,使您能够初始化 Application
变量(在应用程序关闭时释放资源)并响应 Application
错误
Application_Start
:应用程序启动时触发。这是初始化Application
变量的完美位置。Application_End
:应用程序关闭时触发。使用此方法释放应用程序资源并执行日志记录。Application_Error
:发生未处理错误时触发。使用此方法执行错误日志记录。
现在让我们将回发信息存储在应用程序状态中
//global.asax
void Application_Start(object sender, EventArgs e)
{
Application["number"] = 0;
}
//In web pages
Application.Lock();
Application["number"] = Convert.ToInt32(Application["number"]) + 1;
Application.UnLock();
Label5.Text = Application["number"].ToString();
当我们运行页面并点击按钮进行回发时,网页将向我们显示目前已完成的回发次数,这些回发次数存储在 ApplicationState
中。我们可以使用此对象来跟踪网站上所有用户的点击次数(详细信息请参见代码)。
会话状态
与 Application
状态类似,此信息也位于一个全局存储中,可从 Web 应用程序中的所有页面访问。Session
状态存储在 Sessionkey
/value dictionary
中。此信息将仅对当前用户可用,即仅对当前会话可用。
//global.asax
void Session_Start(object sender, EventArgs e)
{
// Code that runs when a new session is started
Session["number"] = 0;
}
// Web forms
Session["number"] = Convert.ToInt32(Session["number"]) + 1;
Label6.Text = Session["number"].ToString();
当我们运行页面并点击按钮进行回发时,网页将向我们显示目前已完成的回发次数,这些回发次数存储在 SessionState
中。我们可以使用此对象来跟踪当前用户(即拥有整个网站会话的用户)的点击次数(详细信息请参见代码)。
客户端状态管理的优点
- 更好的可伸缩性
- 支持多个浏览器
服务器端状态管理的优点
- 更好的安全性
- 减少带宽
关注点
我为我的一些学生创建了这个小文档,他们想要一些关于状态管理的复习材料。我想我会把它分享在 CodeProject 上,因为一些初学者可能会在其中找到一些有用的东西。现在,我将总结状态管理技术如下:
- 需要可伸缩性时应使用客户端状态管理。
- 需要数据安全时应使用服务器端状态管理。
- ASP.NET 使用的默认页面级状态管理技术是
ViewState
。 - 我们可以将自定义信息存储在
viewState
中。 - 自定义控件使用
ControlState
存储信息。 - 隐藏字段也可以用于存储同一页面的信息。
- Cookies 可用于存储同一台计算机多次访问所需的信息。
- 查询字符串可用于在页面之间传递信息。
ApplicationState
应该用于存储所有网页和所有用户都可以访问的信息。SessionState
应该用于存储所有网页都可以访问但仅限于当前用户/会话的信息。
历史
- 2012 年 2 月 17 日:ASP.NET 状态管理新手介绍,带有一些不切实际的示例