代码改变世界

ASP.NET状态管理

2009-02-11 12:58  宝宝合凤凰  阅读(580)  评论(0)    收藏  举报

ASP.NET状态管理之一(概括篇)

每次将网页发送到服务器时,都会创建网页类的一个新实例。在传统的Web编程中,这通常意味着在每一次往返行程中,与该页及该页上的控件相关联的所有信息都会丢失。例如,如果用户将信息输入到文本框,该信息将在从浏览器或客户端设备到服务器的往返行程中丢失。

状态管理是您对同一页或不同页的多个请求维护状态和页信息的过程。与所有基于 HTTP 的技术一样,Web 窗体页是无状态的,这意味着它们不自动指示序列中的请求是否全部来自相同的客户端,或者单个浏览器实例是否一直在查看页或站点。此外,到服务器的每一往返过程都将销毁并重新创建页;因此,如果超出了单个页的生命周期,页信息将不存在。

基于客户端的状态管理
视图状态
ViewState 属性提供一个字典对象,用于在对同一页的多个请求之间保留值。这是页用来在往返行程之间保留页和控件属性值的默认方法。
在处理页时,页和控件的当前状态会散列为一个字符串,并在页中保存为一个隐藏域或多个隐藏域(如果存储在 ViewState 属性中的数据量超过了 MaxPageStateFieldLength 属性中的指定值)。当将页回发到服务器时,页会在页初始化阶段分析视图状态字符串,并还原页中的属性信息。
ASP.NET状态管理之十一(视图状态ViewState)


控件状态
有时,为了让控件正常工作,您需要按顺序存储控件状态数据。例如,如果编写了一个自定义控件,其中使用了不同的选项卡来显示不同的信息。为了让自定义控件按预期的方式工作,该控件需要知道在往返行程之间选择了哪个选项卡。可以使用 ViewState 属性来实现这一目的,不过,开发人员可以在页级别关闭视图状态,从而使控件无法正常工作。为了解决此问题,ASP.NET 页框架在 ASP.NET 中公开了一项名为控件状态的功能。
ControlState 属性允许您保持特定于某个控件的属性信息,且不能像 ViewState 属性那样被关闭。
ASP.NET状态管理之十二(控件状态ControlState)

隐藏域
ASP.NET 允许您将信息存储在 HiddenField 控件中,此控件将呈现为一个标准的 HTML 隐藏域。隐藏域在浏览器中不以可见的形式呈现,但您可以就像对待标准控件一样设置其属性。当向服务器提交页时,隐藏域的内容将在 HTTP 窗体集合中随同其他控件的值一起发送。隐藏域可用作一个储存库,您可以将希望直接存储在页中的任何特定于页的信息放置到其中。
ASP.NET状态管理之三(隐藏域HiddenField)

 


Cookie
Cookie是一些少量的数据,这些数据或者存储在客户端文件系统的文本文件中,或者存储在客户端浏览器会话的内存中。Cookie包含特定于站点的信息,这些信息是随页输出一起由服务器发送到客户端的。Cookie可以是临时的(具有特定的过期时间和日期),也可以是永久的。

可以使用Cookie来存储有关特定客户端、会话或应用程序的信息。Cookie保存在客户端设备上,当浏览器请求某页时,客户端会将Cookie中的信息连同请求信息一起发送。服务器可以读取Cookie并提取它的值。一项常见的用途是存储标记(可能已加密),以指示该用户已经在您的应用程序中进行了身份验证。
ASP.NET状态管理之五(Cookie)

 


查询字符串
查询字符串是在页 URL 的结尾附加的信息
ASP.NET状态管理之二(查询字苻串QueryString)

 


基于服务器的状态管理

应用程序状态
ASP.NET 允许您使用应用程序状态来保存每个活动的 Web 应用程序的值,应用程序状态是 HttpApplicationState 类的一个实例。应用程序状态是一种全局存储机制,可从 Web 应用程序中的所有页面访问。
应用程序状态存储在一个键/值字典中,在每次请求一个特定的 URL 期间就会创建这样一个字典。可以将特定于应用程序的信息添加到此结构以在页请求期间存储它。
一旦将应用程序特定的信息添加到应用程序状态中,服务器就会管理该对象。
ASP.NET状态管理之八(应用程序Application)

 

 

暂存状态
获取可用于在 HTTP 请求过程中在 IHttpModule 和 IHttpHandler 之间组织和共享数据的键值
ASP.NET状态管理之四(暂存状态HttpContext.Items)

 

会话
ASP.NET 允许您使用会话状态保存每个活动的 Web 应用程序会话的值,会话状态是 HttpSessionState 类的一个实例。
会话状态与应用程序状态相似,不同的只是会话状态的范围限于当前的浏览器会话。如果有不同的用户在使用您的应用程序,则每个用户会话都将有一个不同的会话状态。此外,如果同一用户在退出后又返回到应用程序,第二个用户会话的会话状态也会与第一个不同。
会话状态采用键/值字典形式的结构来存储特定于会话的信息,这些信息需要在服务器往返行程之间及页请求之间进行维护。
可以使用会话状态来完成以下任务:
1.唯一标识浏览器或客户端设备请求,并将这些请求映射到服务器上的单独会话实例。
2.在服务器上存储特定于会话的数据,以用于同一个会话内的多个浏览器或客户端设备请求。
3.引发适当的会话管理事件。此外,可以利用这些事件编写应用程序代码。
一旦将应用程序特定的信息添加到会话状态中,服务器就会管理该对象。根据您指定的选项的不同,可以将会话信息存储在 Cookie 中、进程外服务器中或运行 Microsoft SQL Server 的计算机中。
ASP.NET状态管理之九(会话Session)

 

缓存
通常,应用程序可以将那些频繁访问的数据,以及那些需要大量处理时间来创建的数据存储在内存中,从而提高性能。例如,如果应用程序使用复杂的逻辑来处理大量数据,然后再将数据作为用户频繁访问的报表返回,避免在用户每次请求数据时重新创建报表可以提高效率。同样,如果应用程序包含一个处理复杂数据但不需要经常更新的页,则在每次请求时服务器都重新创建该页会使工作效率低下。
在这些情况下,为了帮助您提高应用程序的性能,ASP.NET 使用两种基本的缓存机制来提供缓存功能。第一种机制是应用程序缓存,它允许您缓存所生成的数据,如 DataSet 或自定义报表业务对象。第二种机制是页输出缓存,它保存页处理输出,并在用户再次请求该页时,重用所保存的输出,而不是再次处理该页。
ASP.NET状态管理之六(缓存Cache)

 

配置文件
在许多应用程序中,需要存储并使用对用户唯一的信息。用户访问站点时,您可以使用已存储的信息向用户显示 Web 应用程序的个性化版本。个性化应用程序需要大量的元素:必须使用唯一的用户标识符存储信息,能够在用户再次访问时识别用户,然后根据需要获取用户信息。若要简化应用程序,可以使用 ASP.NET 配置文件功能,该功能可为您执行所有上述任务。
ASP.NET 配置文件功能将信息与单个用户关联,并采用持久性的格式存储这些信息。通过配置文件,可以管理用户信息,而无需创建和维护自己的数据库。此外,通过使用可从应用程序中的任何位置访问的强类型 API,就可以借助 ASP.NET 配置文件功能使用相关的用户信息。
可以使用配置文件存储任何类型的对象。配置文件功能提供了一项通用存储功能,使您能够定义和维护几乎任何类型的数据,同时仍可用类型安全的方式使用数据。
ASP.NET状态管理之十(配置文件Profile)

 

 

 

各种状态使用总结
ASP.NET状态管理之十三(总结)

 

+++++++++++++++++++++++++++++++++++++++

 

ASP.NET状态管理之十一(视图状态ViewState)


视图状态是 ASP.NET 页框架默认情况下用于保存往返过程之间的页和控件值的方法。当呈现页的 HTML 形式时,需要在回发过程中保留的页的当前状态和值将被序列化为 Base64 编码的字符串,并输出到视图状态的隐藏字段中。

视图状态是 ASP.NET 页中的储存库,可以存储需要在回发过程中保留的值。例如,您可以将信息存储在视图状态中,下次将页面发送到服务器时,将会在页加载事件过程中访问这些信息。
您可以使用页的 ViewState 属性(此属性公开一个字典对象)来访问视图状态信息。由于视图状态数据以字符串的形式存储,因此只能存储可以序列化的对象。

视图状态数据以 Base64 编码字符串的格式存储在一个或多个隐藏字段中。您可以使用页的 ViewState 属性(此属性公开一个字典对象)来访问视图状态信息。由于视图状态数据以字符串的形式存储,因此只能存储可以序列化的对象。

由于视图状态是作为隐藏字段发送的,因此直到发生 PreRenderComplete 事件之前,都可以对视图状态进行更改。一旦将页呈现到浏览器,便无法保存对视图状态的更改。

如果查看页输出源,则可以看到隐藏视图状态字段中的信息,这可能产生安全性问题。若要缓解该问题,可以通过将 @ Page 指令中的 viewStateEncryptionMode 属性设置为“Always”来加密视图状态。

注意
若要使用 ViewState 属性,ASP.NET 网页必须包含具有 runat="server" 属性的窗体元素。

 

 

可以存储在视图状态中的数据类型
您可以将下列类型的对象存储到视图状态中:
字符串
整数
布尔值
Array 对象
ArrayList 对象
哈希表
自定义类型转换器(请参见 TypeConverter 类)
您也可以存储其他类型的数据,但是必须使用 Serializable 属性编译该类,这样视图状态便可以将这些数据序列化为 XML。

 

使用视图状态的注意事项
视图状态提供了特定 ASP.NET 页的状态信息。如果需要在多个页上使用信息,或者如果需要在访问网站时保留信息,则应当使用另一个方法(如应用程序状态、会话状态或个性化设置)来维护状态。

视图状态信息将序列化为 XML,然后使用 Base64 编码进行编码,这将生成大量的数据。将页回发到服务器时,视图状态的内容将作为页回发信息的一部分发送。如果视图状态包含大量信息,则会影响页的性能。

另一个重要的考虑因素是,如果隐藏字段中的数据量过大,某些代理和防火墙将禁止访问包含这些数据的页。由于最大数量会随所采用的防火墙和代理的不同而不同,因此大量隐藏字段可能会导致偶发性问题。为了帮助避免这一问题,如果 ViewState 属性中存储的数据量超过了页的 MaxPageStateFieldLength 属性中指定的值,该页会将视图状态拆分为多个隐藏字段,以将每个单独字段的大小减少到防火墙拒绝的大小以下。

某些移动设备根本不允许使用隐藏字段。因此,视图状态对于这些设备无效。

 

从视图状态中读取值
在页代码中,设置 ViewState 属性中变量的值。
下面的代码示例说明如何从视图状态中获取名为 arrayListInViewState 的 ArrayList 对象,然后将 GridView 控件作为数据源绑定到该对象。
arrayList = new ArrayList();
arrayList = (ArrayList)ViewState["arrayListInViewState"];

this.GridView1.DataSource = arrayList;
this.GridView1.DataBind();

视图状态中的值被类型化为 String。如本例所示。在 C# 中,当您读取视图状态值时,应该始终强制转换为适当的类型。
如果尝试从不存在的视图状态中获取值,则不会引发任何异常。若要确保所需的值处于视图状态,请首先使用测试(例如以下测试)检查该对象是否存在:
if (ViewState["color"] == null)
    // No such value in view state, take appropriate action.

 

将值保存到视图状态
下面的示例演示如何实现从其控件的 ViewState 属性存储和检索值的 Text 属性。
private const int defaultFontSize = 3;

// Add property values to view state with set;
// retrieve them from view state with get.
public String Text
{
    get
    {
        object o = ViewState["text"];
        return (o == null)? String.Empty : (string)o;
    }

    set
    {
        ViewState["Text"] = value;
    }
}

加密视图状态
在 @ Page 指令中,将 ViewStateEncryptionMode 属性设置为“Always”,如下面的示例所示:
<% @Page ViewStateEncryptionMode="Always" ...  %>

 

===========================================

ASP.NET状态管理之十二(控件状态ControlState)

 

如果您在编写控件,可以将控件的状态信息存储在 ViewState 字典中,该字典是一个 StateBag 对象。开发人员将通过 ControlState 属性检索控件状态。您为 ViewState 属性指定键和值,Page 对象将对请求之间的状态信息进行序列化。要在您的控件中执行自定义状态处理,可重写 LoadViewState 和 SaveViewState 方法。在页开发人员禁用视图状态时,存储在此字典中的所有状态信息都会丢失。为弥补这一缺陷,在 ASP.NET 2.0 版中,您可以将关键状态信息存储在一个称为控件状态的单独对象中。在页开发人员禁用视图状态时,控件状态对象不会受到影响。要将状态信息存储在控件状态对象中,控件需要重写 LoadControlState 和 SaveControlState 方法,并且需要在每次初始化该控件时将其注册为在控件状态中存储状态信息。可以通过重写 OnInit 方法并调用 RegisterRequiresControlState 方法将控件注册为使用控件状态。
控件状态是一个包含 Web 服务器控件正常工作所需要的关键视图状态数据的对象,并且它包含在一个单独的对象(与包含普通视图状态信息的对象不同)中。在 Page 级别禁用视图状态时,控件状态数据不会受到影响,但是需要额外的实现步骤才能使用。

下面的代码示例演示一个从 PageStatePersister 类派生的类如何初始化 ControlState 属性。此示例将 ControlState 属性指派给 Pair 对象的 Second 字段,并使用 ObjectStateFormatter 类对该属性进行序列化。调用 Load 方法时,使用 ObjectStateFormatter 类对视图状态和控件状态信息进行反序列化,并使用所得 Pair 对象的 Second 字段初始化 ControlState 属性。
//
// Load ViewState and ControlState.
//
public override void Load()
{
    Stream stateStream = GetSecureStream();

    // Read the state string, using the StateFormatter.
    StreamReader reader = new StreamReader(stateStream);

    IStateFormatter formatter = this.StateFormatter;
    string fileContents = reader.ReadToEnd();

    // Deserilize returns the Pair object that is serialized in
    // the Save method.
    Pair statePair = (Pair)formatter.Deserialize(fileContents);

    ViewState = statePair.First;
    ControlState = statePair.Second;
    reader.Close();
    stateStream.Close();
}

 

 

+++++++++++++++++++++++++++

ASP.NET状态管理之三(隐藏域HiddenField)

 

ASP.NET 允许您将信息存储在 HiddenField 控件中,此控件将呈现为一个标准的 HTML 隐藏域。
隐藏域在浏览器中不以可见的形式呈现,但您可以就像对待标准控件一样设置其属性。
当向服务器提交页时,隐藏域的内容将在 HTTP 窗体集合中随同其他控件的值一起发送。
隐藏域可用作一个储存库,您可以将希望直接存储在页中的任何特定于页的信息放置到其中。

安全注意
恶意用户可以很容易地查看和修改隐藏域的内容。请不要在隐藏域中存储任何敏感信息或保障应用程序正确运行的信息。

HiddenField 控件在其Value属性中只存储一个变量,并且必须通过显式方式添加到页上。
为了在页处理期间能够使用隐藏域的值,必须使用 HTTP POST 命令提交相应的页。如果在您使用隐藏域的同时,为了响应某个链接或 HTTP GET 命令而对页进行了相应处理,那么隐藏域将不可用。

HiddenField 控件用于存储一个值,在向服务器的各次发送过程中,需保持该值。它呈现为 <input type= "hidden"/> 元素。
通常情况下,Web 窗体页的状态由视图状态、会话状态和 cookie 来维持。但是,如果这些方法被禁用或不可用,则可以使用 HiddenField 控件来存储状态值。

若要指定 HiddenField 控件的值,请使用 Value 属性。

在向服务器的各次发送过程中,当 HiddenField 控件的值更改时,将引发ValueChanged事件

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>无标题页</title>

    <script type="text/javascript">
    function PageLoad()
  {
 
    // Set the value of the HiddenField control with the
    // value from the TextBox.
    form1.ValueHiddenField.value = form1.ValueTextBox.value;
   
  }

    </script>

</head>
<body>
    <form id="form1" runat="server">
        <div>
            <asp:TextBox ID="ValueTextBox" runat="server" />
            <br />
            <input type="submit" name="SubmitButton" value="Submit" onclick="PageLoad()" />
            <br />
            <asp:Label ID="Message" runat="server" />
            <asp:HiddenField ID="ValueHiddenField" runat="server" OnValueChanged="ValueHiddenField_ValueChanged" />
        </div>
    </form>
</body>
</html>

 

 

 

 

总结

可以在页上的隐藏域中存储特定于页的信息,作为维护页的状态的一种方式。
如果使用隐藏域,最好在客户端上只存储少量经常更改的数据。

注意:
如果使用隐藏域,则必须使用 HTTP POST 方法向服务器提交页,而不是使用通过页 URL 请求该页的方法(HTTP GET 方法)向服务器提交页。
 

使用隐藏域的优点
1.不需要任何服务器资源:隐藏域在页上存储和读取。
2.广泛的支持:几乎所有浏览器和客户端设备都支持具有隐藏域的窗体。
3.实现简单:隐藏域是标准的HTML控件,不需要复杂的编程逻辑。

使用隐藏域的缺点
1.潜在的安全风险  
隐藏域可以被篡改。如果直接查看页输出源,可以看到隐藏域中的信息,这导致潜在的安全性问题。
您可以手动加密和解密隐藏域的内容,但这需要额外的编码和开销。如果关注安全,请考虑使用基于服务器的状态机制,从而不将敏感信息发送到客户端。
2.简单的存储结构  
隐藏域不支持复杂数据类型。
隐藏域只提供一个字符串值域存放信息。
若要存储多个值,必须实现分隔的字符串以及用来分析那些字符串的代码。您可以手动分别将复杂数据类型序列化为隐藏域以及将隐藏域反序列化为复杂数据类型。但是,这需要额外的代码来实现。
如果您需要将复杂数据类型存储在客户端上,请考虑使用视图状态。视图状态内置了序列化,并且将数据存储在隐藏域中。
3.性能注意事项  
由于隐藏域存储在页本身,因此如果存储较大的值,用户显示页和发送页时的速度可能会减慢。
4.存储限制  
如果隐藏域中的数据量过大,某些代理和防火墙将阻止对包含这些数据的页的访问。因为最大数量会随所采用的防火墙和代理的不同而不同,较大的隐藏域可能会出现偶发性问题。
如果您需要存储大量的数据项,请考虑执行下列操作之一:
(1)将每个项放置在单独的隐藏域中。
(2)使用视图状态并打开视图状态分块,这样会自动将数据分割到多个隐藏域。
(3)不将数据存储在客户端上,将数据保留在服务器上。向客户端发送的数据越多,您的应用程序的表面响应时间越慢,因为浏览器需要下载或发送更多的数据。
 

++++++++++++++++++++++++++++++++++++++++

ASP.NET状态管理之五(Cookie)

Cookie 提供了一种在 Web 应用程序中存储用户特定信息的方法。
例如,当用户访问您的站点时,您可以使用 Cookie 存储用户首选项或其他信息。当该用户再次访问您的网站时,应用程序便可以检索以前存储的信息。

什么是 Cookie?
Cookie 是一小段文本信息,伴随着用户请求和页面在 Web 服务器和浏览器之间传递。
Cookie 包含每次用户访问站点时 Web 应用程序都可以读取的信息。

例如,如果在用户请求站点中的页面时应用程序发送给该用户的不仅仅是一个页面,还有一个包含日期和时间的 Cookie,用户的浏览器在获得页面的同时还获得了该 Cookie,并将它存储在用户硬盘上的某个文件夹中。

以后,如果该用户再次请求您站点中的页面,当该用户输入 URL 时,浏览器便会在本地硬盘上查找与该 URL 关联的 Cookie。如果该 Cookie 存在,浏览器便将该 Cookie 与页请求一起发送到您的站点。然后,应用程序便可以确定该用户上次访问站点的日期和时间。您可以使用这些信息向用户显示一条消息,也可以检查到期日期。

Cookie 与网站关联,而不是与特定的页面关联。因此,无论用户请求站点中的哪一个页面,浏览器和服务器都将交换 Cookie 信息。用户访问不同站点时,各个站点都可能会向用户的浏览器发送一个 Cookie;浏览器会分别存储所有 Cookie。

Cookie 帮助网站存储有关访问者的信息。一般来说,Cookie 是一种保持 Web 应用程序连续性(即执行状态管理)的方法。除短暂的实际交换信息的时间外,浏览器和 Web 服务器间都是断开连接的。对于用户向 Web 服务器发出的每个请求,Web 服务器都会单独处理。但是在很多情况下,Web 服务器在用户请求页时识别出用户会十分有用。例如,购物站点上的 Web 服务器跟踪每位购物者,这样站点就可以管理购物车和其他的用户特定信息。因此,Cookie 可以作为一种名片,提供相关的标识信息帮助应用程序确定如何继续执行。

使用 Cookie 能够达到多种目的,所有这些目的都是为了帮助网站记住用户。例如,一个实施民意测验的站点可以简单地将 Cookie 作为一个 Boolean 值,用它来指示用户的浏览器是否已参与了投票,这样用户便无法进行第二次投票。要求用户登录的站点则可以通过 Cookie 来记录用户已经登录,这样用户就不必每次都输入凭据。

Cookie 的限制
大多数浏览器支持最大为 4096 字节的 Cookie。由于这限制了 Cookie 的大小,最好用 Cookie 来存储少量数据,或者存储用户 ID 之类的标识符。用户 ID 随后便可用于标识用户,以及从数据库或其他数据源中读取用户信息。(有关存储用户信息安全建议的信息,请参见下面的“Cookie 和安全性”一节。)

浏览器还限制站点可以在用户计算机上存储的 Cookie 的数量。大多数浏览器只允许每个站点存储 20 个 Cookie;如果试图存储更多 Cookie,则最旧的 Cookie 便会被丢弃。有些浏览器还会对它们将接受的来自所有站点的 Cookie 总数作出绝对限制,通常为 300 个。

您可能遇到的 Cookie 限制是用户可以将其浏览器设置为拒绝接受 Cookie。如果定义一个 P3P 隐私策略,并将其放置在网站的根目录中,则更多的浏览器将接受您站点的 Cookie。但是,您可能会不得不完全放弃 Cookie,而通过其他机制来存储用户特定的信息。

编写 Cookie
浏览器负责管理用户系统上的 Cookie。
Cookie 通过 HttpResponse 对象发送到浏览器,该对象公开称为 Cookies 的集合。可以将 HttpResponse 对象作为 Page 类的 Response 属性来访问。要发送给浏览器的所有 Cookie 都必须添加到此集合中。创建 Cookie 时,需要指定 Name 和 Value。每个 Cookie 必须有一个唯一的名称,以便以后从浏览器读取 Cookie 时可以识别它。由于 Cookie 按名称存储,因此用相同的名称命名两个 Cookie 会导致其中一个 Cookie 被覆盖。

还可以设置 Cookie 的到期日期和时间。用户访问编写 Cookie 的站点时,浏览器将删除过期的 Cookie。只要应用程序认为 Cookie 值有效,就应将 Cookie 的有效期设置为这一段时间。对于永不过期的 Cookie,可将到期日期设置为从现在起 50 年。

注意:
用户可随时清除其计算机上的 Cookie。即便存储的 Cookie 距到期日期还有很长时间,但用户还是可以决定删除所有 Cookie,清除 Cookie 中存储的所有设置。
 

如果没有设置 Cookie 的有效期,仍会创建 Cookie,但不会将其存储在用户的硬盘上。而会将 Cookie 作为用户会话信息的一部分进行维护。当用户关闭浏览器时,Cookie 便会被丢弃。这种非永久性 Cookie 很适合用来保存只需短时间存储的信息,或者保存由于安全原因不应该写入客户端计算机上的磁盘的信息。例如,如果用户在使用一台公用计算机,而您不希望将 Cookie 写入该计算机的磁盘中,这时就可以使用非永久性 Cookie。

可以通过多种方法将 Cookie 添加到 Cookies 集合中。下面的示例演示两种编写 Cookie 的方法:
Response.Cookies["userName"].Value = "patrick";
Response.Cookies["userName"].Expires = DateTime.Now.AddDays(1);

HttpCookie aCookie = new HttpCookie("lastVisit");
aCookie.Value = DateTime.Now.ToString();
aCookie.Expires = DateTime.Now.AddDays(1);
Response.Cookies.Add(aCookie);

此示例向 Cookies 集合添加两个 Cookie,一个名为 userName,另一个名为 lastVisit。
对于第一个 Cookie,Cookies 集合的值是直接设置的。可以通过这种方式向集合添加值,因为 Cookies 是从 NameObjectCollectionBase 类型的专用集合派生的。

对于第二个 Cookie,代码创建了一个 HttpCookie 类型的对象实例,设置其属性,然后通过 Add 方法将其添加到 Cookies 集合。在实例化 HttpCookie 对象时,必须将该 Cookie 的名称作为构造函数的一部分进行传递。

这两个示例都完成了同一任务,即向浏览器写入一个 Cookie。在这两种方法中,有效期值必须为 DateTime 类型。但是,lastVisited 值也是日期时间值。因为所有 Cookie 值都存储为字符串,因此,必须将日期时间值转换为 String。

多值 Cookie
可以在 Cookie 中存储一个值,如用户名和上次访问时间。
也可以在一个 Cookie 中存储多个名称/值对。名称/值对称为子键。(子键布局类似于 URL 中的查询字符串。)例如,不要创建两个名为 userName 和 lastVisit 的单独 Cookie,而可以创建一个名为 userInfo 的 Cookie,其中包含两个子键 userName 和 lastVisit。

您可能会出于多种原因来使用子键。首先,将相关或类似的信息放在一个 Cookie 中很方便。此外,由于所有信息都在一个 Cookie 中,所以诸如有效期之类的 Cookie 属性就适用于所有信息。(反之,如果要为不同类型的信息指定不同的到期日期,就应该把信息存储在单独的 Cookie 中。)

带有子键的 Cookie 还可帮助您限制 Cookie 文件的大小。正如前面“Cookie 的限制”一节中所提到的,Cookie 通常限制为 4096 字节,并且每个站点最多可存储 20 个 Cookie。使用带子键的单个 Cookie,使用的 Cookie 数就不会超过分配给站点的 20 个的限制。此外,一个 Cookie 会占用大约 50 个字符的系统开销(用于保存有效期信息等),再加上其中存储的值的长度,其总和接近 4096 字节的限制。如果存储五个子键而不是五个单独的 Cookie,便可节省单独 Cookie 的系统开销,节省大约 200 字节。

若要创建带子键的 Cookie,您可以使用编写单个 Cookie 的各种语法。
下面的示例演示用于编写同一 Cookie 的两种方法,其中的每个 Cookie 都带有两个子键:
Response.Cookies["userInfo"]["userName"] = "patrick";
Response.Cookies["userInfo"]["lastVisit"] = DateTime.Now.ToString();
Response.Cookies["userInfo"].Expires = DateTime.Now.AddDays(1);

HttpCookie aCookie = new HttpCookie("userInfo");
aCookie.Values["userName"] = "patrick";
aCookie.Values["lastVisit"] = DateTime.Now.ToString();
aCookie.Expires = DateTime.Now.AddDays(1);
Response.Cookies.Add(aCookie);

控制 Cookie 的范围
默认情况下,一个站点的全部 Cookie 都一起存储在客户端上,而且所有 Cookie 都会随着对该站点发送的任何请求一起发送到服务器。也就是说,一个站点中的每个页面都能获得该站点的所有 Cookie。
但是,可以通过两种方式设置 Cookie 的范围:
1.将 Cookie 的范围限制到服务器上的某个文件夹,这允许您将 Cookie 限制到站点上的某个应用程序。
2.将范围设置为某个域,这允许您指定域中的哪些子域可以访问 Cookie。

将 Cookie 限制到某个文件夹或应用程序
若要将 Cookie 限制到服务器上的某个文件夹,请按下面的示例设置 Cookie 的 Path 属性:
HttpCookie appCookie = new HttpCookie("AppCookie");
appCookie.Value = "written " + DateTime.Now.ToString();
appCookie.Expires = DateTime.Now.AddDays(1);
appCookie.Path = "/Application1";
Response.Cookies.Add(appCookie);

路径可以是站点根目录下的物理路径,也可以是虚拟根目录。所产生的效果是 Cookie 只能用于 Application1 文件夹或虚拟根目录中的页面。例如,如果您的站点名称为 http://www.contoso.com/,则在前面示例中创建的 Cookie 将只能用于路径为 http://www.contoso.com/Application1/ 的页面以及该文件夹下的所有页面。但是,Cookie 将不能用于其他应用程序中的页面,如 http://www.contoso.com/Application2/http://www.contoso.com/ 中的页面。

注意:
在某些浏览器中,路径区分大小写。您无法控制用户如何在其浏览器中键入 URL,但如果应用程序依赖于与特定路径相关的 Cookie,请确保您创建的所有超链接中的 URL 与 Path 属性值的大小写相匹配。

默认情况下,一个站点的全部 Cookie 都一起存储在客户端上,而且所有 Cookie 都会随着对该站点发送的任何请求一起发送到服务器。也就是说,一个站点中的每个页面都能获得该站点的所有 Cookie。但是,可以通过两种方式设置 Cookie 的范围:

将 Cookie 的范围限制到服务器上的某个文件夹,这允许您将 Cookie 限制到站点上的某个应用程序。

将范围设置为某个域,这允许您指定域中的哪些子域可以访问 Cookie。

将 Cookie 限制到某个文件夹或应用程序
若要将 Cookie 限制到服务器上的某个文件夹,请按下面的示例设置 Cookie 的 Path 属性:
HttpCookie appCookie = new HttpCookie("AppCookie");
appCookie.Value = "written " + DateTime.Now.ToString();
appCookie.Expires = DateTime.Now.AddDays(1);
appCookie.Path = "/Application1";
Response.Cookies.Add(appCookie);
 
注意
还可以通过将 Cookie 直接添加到 Cookies 集合的方式来编写 Cookie,如先前的示例所示。
 

路径可以是站点根目录下的物理路径,也可以是虚拟根目录。所产生的效果是 Cookie 只能用于 Application1 文件夹或虚拟根目录中的页面。例如,如果您的站点名称为 http://www.contoso.com/,则在前面示例中创建的 Cookie 将只能用于路径为 http://www.contoso.com/Application1/ 的页面以及该文件夹下的所有页面。但是,Cookie 将不能用于其他应用程序中的页面,如 http://www.contoso.com/Application2/http://www.contoso.com/ 中的页面。

注意
在某些浏览器中,路径区分大小写。您无法控制用户如何在其浏览器中键入 URL,但如果应用程序依赖于与特定路径相关的 Cookie,请确保您创建的所有超链接中的 URL 与 Path 属性值的大小写相匹配。
 

限制 Cookie 的域范围
默认情况下,Cookie 与特定域关联。
例如,如果您的站点是 http://www.contoso.com/,那么当用户向该站点请求任何页时,您编写的 Cookie 就会被发送到服务器。(这可能不包括带有特定路径值的 Cookie。)如果站点具有子域(例如,contoso.com、sales.contoso.com 和 support.contoso.com),则可以将 Cookie 与特定的子域关联。
若要执行此操作,请设置 Cookie 的 Domain 属性,如此示例所示:
Response.Cookies["domain"].Value = DateTime.Now.ToString();
Response.Cookies["domain"].Expires = DateTime.Now.AddDays(1);
Response.Cookies["domain"].Domain = "support.contoso.com";

当以此方式设置域时,Cookie 将仅可用于指定的子域中的页面。还可以使用 Domain 属性创建可在多个子域间共享的 Cookie,如下面的示例所示:
Response.Cookies["domain"].Value = DateTime.Now.ToString();
Response.Cookies["domain"].Expires = DateTime.Now.AddDays(1);
Response.Cookies["domain"].Domain = "contoso.com";

随后 Cookie 将可用于主域,也可用于 sales.contoso.com 和 support.contoso.com 域。

读取 Cookie
浏览器向服务器发出请求时,会随请求一起发送该服务器的 Cookie。
在 ASP.NET 应用程序中,可以使用 HttpRequest 对象读取 Cookie,该对象可用作 Page 类的 Request 属性使用。HttpRequest 对象的结构与 HttpResponse 对象的结构基本相同,因此,可以从 HttpRequest 对象中读取 Cookie,并且读取方式与将 Cookie 写入 HttpResponse 对象的方式基本相同。
下面的代码示例演示两种方法,通过这两种方法可获取名为 username 的 Cookie 的值,并将其值显示在 Label 控件中:
if(Request.Cookies["userName"] != null)
    Label1.Text = Server.HtmlEncode(Request.Cookies["userName"].Value);

if(Request.Cookies["userName"] != null)
{
    HttpCookie aCookie = Request.Cookies["userName"];
    Label1.Text = Server.HtmlEncode(aCookie.Value);
}

在尝试获取 Cookie 的值之前,应确保该 Cookie 存在;如果该 Cookie 不存在,将会收到 NullReferenceException 异常。还请注意在页面中显示 Cookie 的内容前,先调用 HtmlEncode 方法对 Cookie 的内容进行编码。这样可以确保恶意用户没有向 Cookie 中添加可执行脚本。

注意
由于不同的浏览器存储 Cookie 的方式不同,因此,同一计算机上的不同浏览器没有必要能够读取彼此的 Cookie。例如,如果使用 Internet Explorer 测试一个页面,然后再使用其他浏览器进行测试,那么后者将不会找到 Internet Explorer 保存的 Cookie。

读取 Cookie 中子键值的方法与设置该值的方法类似。下面的代码示例演示获取子键值的一种方法:
if(Request.Cookies["userInfo"] != null)
{
    Label1.Text =
        Server.HtmlEncode(Request.Cookies["userInfo"]["userName"]);

    Label2.Text =
        Server.HtmlEncode(Request.Cookies["userInfo"]["lastVisit"]);
}

在上面的示例中,代码读取子键 lastVisit 的值,该值先前被设置为字符串表示形式的 DateTime 值。Cookie 将值存储为字符串,因此,如果要将 lastVisit 值作为日期使用,必须将其转换为适当的类型,如此示例所示:

DateTime dt;
dt = DateTime.Parse(Request.Cookies["userInfo"]["lastVisit"]);
 
Cookie 中的子键被类型化为 NameValueCollection 类型的集合。因此,获取单个子键的另一种方法是获取子键集合,然后再按名称提取子键值,如下面的示例所示:
if(Request.Cookies["userInfo"] != null)
{
    System.Collections.Specialized.NameValueCollection
        UserInfoCookieCollection;
      
    UserInfoCookieCollection = Request.Cookies["userInfo"].Values;
    Label1.Text =
        Server.HtmlEncode(UserInfoCookieCollection["userName"]);
    Label2.Text =
        Server.HtmlEncode(UserInfoCookieCollection["lastVisit"]);
}

更改 Cookie 的到期日期
浏览器负责管理 Cookie,而 Cookie 的到期时间和日期可帮助浏览器管理 Cookie 的存储。因此,虽然可以读取 Cookie 的名称和值,但无法读取 Cookie 的到期日期和时间。当浏览器向服务器发送 Cookie 信息时,并不包括有效期信息。(Cookie 的 Expires 属性始终返回值为 0 的日期时间值。)如果您担心 Cookie 的到期日期,必须重新设置该 Cookie。

注意
可以在向浏览器发送 Cookie 之前读取已在 HttpResponse 对象中设置的 Cookie 的 Expires 属性。但是,您无法从返回的 HttpRequest 对象中获取有效期。

读取 Cookie 集合
有时,您可能需要读取可供页面使用的所有 Cookie。若要读取可供页面使用的所有 Cookie 的名称和值,可以使用如下代码依次通过 Cookies 集合。
System.Text.StringBuilder output = new System.Text.StringBuilder();
HttpCookie aCookie;
for(int i=0; i<Request.Cookies.Count; i++)
{
    aCookie = Request.Cookies[i];
    output.Append("Cookie name = " + Server.HtmlEncode(aCookie.Name)
        + "<br />");
    output.Append("Cookie value = " + Server.HtmlEncode(aCookie.Value)
        + "<br /><br />");
}
Label1.Text = output.ToString();

注意
在运行此代码时,可能会看到一个名为 ASP.NET_SessionId 的 Cookie。ASP.NET 使用该 Cookie 来存储您的会话的唯一标识符。会话 Cookie 不会保存在您的硬盘上。

上面的示例有一个限制:如果 Cookie 有子键,则会以一个名称/值字符串来显示子键。可以读取 Cookie 的 HasKeys 属性,以确定 Cookie 是否有子键。如果有,则可以读取子键集合以获取各个子键名称和值。可以通过索引值直接从 Values 集合中读取子键值。相应的子键名称可在 Values 集合的 AllKeys 成员中获得,该成员将返回一个字符串数组。还可以使用 Values 集合的 Keys 成员。但是,首次访问 AllKeys 属性时,该属性会被缓存。相比之下,每次访问 Keys 属性时,该属性都生成一个数组。因此在同一页请求的上下文内,在随后访问时,AllKeys 属性要快得多。

下面的示例演示对前一示例的修改。该示例使用 HasKeys 属性来测试是否存在子键,如果检测到子键,便从 Values 集合获取子键:
for(int i=0; i<Request.Cookies.Count; i++)
{
    aCookie = Request.Cookies[i];
    output.Append("Name = " + aCookie.Name + "<br />");
    if(aCookie.HasKeys)
    {
        for(int j=0; j<aCookie.Values.Count; j++)
        {
            subkeyName = Server.HtmlEncode(aCookie.Values.AllKeys[j]);
            subkeyValue = Server.HtmlEncode(aCookie.Values[j]);
            output.Append("Subkey name = " + subkeyName + "<br />");
            output.Append("Subkey value = " + subkeyValue +
                "<br /><br />");
        }
    }
    else
    {
        output.Append("Value = " + Server.HtmlEncode(aCookie.Value) +
            "<br /><br />");
    }
}
Label1.Text = output.ToString();

或者,可将子键作为 NameValueCollection 对象提取,如下面的示例所示:
System.Text.StringBuilder output = new System.Text.StringBuilder();
HttpCookie aCookie;
string subkeyName;
string subkeyValue;

for (int i = 0; i < Request.Cookies.Count; i++)
{
    aCookie = Request.Cookies[i];
    output.Append("Name = " + aCookie.Name + "<br />");
    if (aCookie.HasKeys)
    {
        System.Collections.Specialized.NameValueCollection CookieValues =
            aCookie.Values;
        string[] CookieValueNames = CookieValues.AllKeys;
        for (int j = 0; j < CookieValues.Count; j++)
        {
            subkeyName = Server.HtmlEncode(CookieValueNames[j]);
            subkeyValue = Server.HtmlEncode(CookieValues[j]);
            output.Append("Subkey name = " + subkeyName + "<br />");
            output.Append("Subkey value = " + subkeyValue +
                "<br /><br />");
        }
    }
    else
    {
        output.Append("Value = " + Server.HtmlEncode(aCookie.Value) +
            "<br /><br />");
    }
}
Label1.Text = output.ToString();

修改和删除 Cookie
不能直接修改 Cookie。更改 Cookie 的过程涉及创建一个具有新值的新 Cookie,然后将其发送到浏览器来覆盖客户端上的旧版本 Cookie。
下面的代码示例演示如何更改存储用户对站点的访问次数的 Cookie 的值:
int counter;
if (Request.Cookies["counter"] == null)
    counter = 0;
else
{
    counter = int.Parse(Request.Cookies["counter"].Value);
}
counter++;

Response.Cookies["counter"].Value = counter.ToString();
Response.Cookies["counter"].Expires = DateTime.Now.AddDays(1);

删除 Cookie
删除 Cookie(即从用户的硬盘中物理移除 Cookie)是修改 Cookie 的一种形式。由于 Cookie 在用户的计算机中,因此无法将其直接移除。但是,可以让浏览器来为您删除 Cookie。该技术是创建一个与要删除的 Cookie 同名的新 Cookie,并将该 Cookie 的到期日期设置为早于当前日期的某个日期。当浏览器检查 Cookie 的到期日期时,浏览器便会丢弃这个现已过期的 Cookie。
下面的代码示例演示删除应用程序中所有可用 Cookie 的一种方法:
HttpCookie aCookie;
string cookieName;
int limit = Request.Cookies.Count;
for (int i=0; i<limit; i++)
{
    cookieName = Request.Cookies[i].Name;
    aCookie = new HttpCookie(cookieName);
    aCookie.Expires = DateTime.Now.AddDays(-1);
    Response.Cookies.Add(aCookie);
}

修改或删除子键
修改单个子键的方法与创建它的方法相同,如下面的示例所示:
Response.Cookies["userInfo"]["lastVisit"] = DateTime.Now.ToString();
Response.Cookies["userInfo"].Expires = DateTime.Now.AddDays(1);

若要删除单个子键,可以操作 Cookie 的 Values 集合,该集合用于保存子键。首先通过从 Cookies 对象中获取 Cookie 来重新创建 Cookie。然后您就可以调用 Values 集合的 Remove 方法,将要删除的子键的名称传递给 Remove 方法。接着,将 Cookie 添加到 Cookies 集合,这样 Cookie 便会以修改后的格式发送回浏览器。下面的代码示例演示如何删除子键。在此示例中,要移除的子键的名称在变量中指定。
string subkeyName;
subkeyName = "userName";
HttpCookie aCookie = Request.Cookies["userInfo"];
aCookie.Values.Remove(subkeyName);
aCookie.Expires = DateTime.Now.AddDays(1);
Response.Cookies.Add(aCookie);

Cookie 和安全性
Cookie 的安全性问题与从客户端获取数据的安全性问题类似。在应用程序中,Cookie 是另一种形式的用户输入,因此很容易被他们非法获取和利用。由于 Cookie 保存在用户自己的计算机上,因此,用户至少能看到您存储在 Cookie 中的数据。用户还可以在浏览器向您发送 Cookie 之前更改该 Cookie。

千万不要在 Cookie 中存储敏感信息,如用户名、密码、信用卡号等等。不要在 Cookie 中放置任何不应由用户掌握的内容,也不要放可能被其他窃取 Cookie 的人控制的内容。

同样,不要轻信从 Cookie 中得到的信息。不要假定数据与您写出时相同;处理 Cookie 值时采用的安全措施应该与处理网页中用户键入的数据时采用的安全措施相同。本主题前面的示例演示在页面中显示值前,先对 Cookie 内容进行 HTML 编码的方法,这与在显示从用户处得到的任何信息之前的做法相同。

Cookie 以明文形式在浏览器和服务器间发送,任何可以截获 Web 通信的人都可以读取 Cookie。可以设置 Cookie 属性,使 Cookie 只能在使用安全套接字层 (SSL) 的连接上传输。SSL 并不能防止保存在用户计算机上的 Cookie 被读取或操作,但可防止 Cookie 在传输过程中被读取,因为 Cookie 已被加密。

确定浏览器是否接受 Cookie
用户可将其浏览器设置为拒绝接受 Cookie。在不能写入 Cookie 时不会引发任何错误。同样,浏览器也不向服务器发送有关其当前 Cookie 设置的任何信息。

注意
Cookies 属性不指示 Cookie 是否启用。它仅指示当前浏览器是否原本支持 Cookie。

确定 Cookie 是否被接受的一种方法是尝试编写一个 Cookie,然后再尝试读取该 Cookie。如果无法读取您编写的 Cookie,则可以假定浏览器不接受 Cookie。

下面的代码示例演示如何测试浏览器是否接受 Cookie。此示例由两个页面组成。第一个页面写出 Cookie,然后将浏览器重定向到第二个页面。第二个页面尝试读取该 Cookie。然后再将浏览器重定向回第一个页面,并将带有测试结果的查询字符串变量添加到 URL。
第一个页面的代码如下所示:
protected void Page_Load(object sender, EventArgs e)
{
    if (!Page.IsPostBack)
    {
        if (Request.QueryString["AcceptsCookies"] == null)
        {
            Response.Cookies["TestCookie"].Value = "ok";
            Response.Cookies["TestCookie"].Expires =
                DateTime.Now.AddMinutes(1);
            Response.Redirect("TestForCookies.aspx?redirect=" +
                Server.UrlEncode(Request.Url.ToString()));
        }
        else
        {
            Label1.Text = "Accept cookies = " +
                Server.UrlEncode(
                Request.QueryString["AcceptsCookies"]);
        }
    }
}

该页面首先测试以确定是不是回发,如果不是,则查找包含测试结果的查询字符串变量名 AcceptsCookies。如果不存在查询字符串变量,表示测试还未完成,因此代码会写出一个名为 TestCookie 的 Cookie。写出 Cookie 后,该示例调用 Redirect 来切换到 TestForCookies.aspx 测试页。附加到测试页 URL 的信息是一个名为 redirect 的查询字符串变量,该变量包含当前页的 URL;这样就能在执行测试后重定向回此页面。

测试页可完全由代码组成;不需要包含控件。下面的代码示例阐释了该测试页。
protected void Page_Load(object sender, EventArgs e)
{
    string redirect = Request.QueryString["redirect"];
    string acceptsCookies;
    if(Request.Cookies["TestCookie"] ==null)
        acceptsCookies = "no";
    else
    {
        acceptsCookies = "yes";
        // Delete test cookie.
        Response.Cookies["TestCookie"].Expires =
            DateTime.Now.AddDays(-1);
    }
    Response.Redirect(redirect + "?AcceptsCookies=" + acceptsCookies,
    true);
}

读取重定向查询字符串变量后,代码尝试读取 Cookie。出于管理目的,如果该 Cookie 存在,则立即删除。测试完成后,代码通过 redirect 查询字符串变量传递给它的 URL 构造一个新的 URL。新 URL 也包括一个包含测试结果的查询字符串变量。最后一步是使用新 URL 将浏览器重定向到最初页面。

该示例的一个改进是可将 Cookie 测试结果保存到永久存储区(如数据库)中,这样就不必在用户每次查看最初页面时都重复进行测试。(默认情况下,在会话状态中存储测试结果需要 Cookie。)

Cookie 和会话状态
当用户导航到您的站点时,服务器为该用户建立唯一的会话,会话将一直延续到用户访问结束。ASP.NET 会为每个会话维护会话状态信息,应用程序可在会话状态信息中存储用户特定信息。

ASP.NET 必须跟踪每个用户的会话 ID,以便可以将用户映射到服务器上的会话状态信息。默认情况下,ASP.NET 使用非永久性 Cookie 来存储会话状态。但是,如果用户已在浏览器上禁用 Cookie,会话状态信息便无法存储在 Cookie 中。

ASP.NET 提供了无 Cookie 会话作为替代。可以将应用程序配置为不将会话 ID 存储在 Cookie 中,而存储在站点中页面的 URL 中。如果应用程序依赖于会话状态,可以考虑将其配置为使用无 Cookie 会话。但是在较少的情况下,如果用户与他人共享 URL(可能是用户将 URL 发送给同事,而该用户的会话仍然处于活动状态),则最终这两个用户可能共享同一个会话,结果将难以预料。

 

总结
使用 Cookie 的优点
1.可配置到期规则:  
Cookie 可以在浏览器会话结束时到期,或者可以在客户端计算机上无限期存在,这取决于客户端的到期规则。
2.不需要任何服务器资源  
Cookie 存储在客户端并在发送后由服务器读取。
3.简单性  
Cookie 是一种基于文本的轻量结构,包含简单的键值对。
4.数据持久性  
虽然客户端计算机上 Cookie 的持续时间取决于客户端上的 Cookie 过期处理和用户干预,Cookie 通常是客户端上持续时间最长的数据保留形式。

使用 Cookie 的缺点
1.大小受到限制  
大多数浏览器对 Cookie 的大小有 4096 字节的限制,尽管在当今新的浏览器和客户端设备版本中,支持 8192 字节的 Cookie 大小已愈发常见。
2.用户配置为禁用  
有些用户禁用了浏览器或客户端设备接收 Cookie 的能力,因此限制了这一功能。
3.潜在的安全风险  
Cookie 可能会被篡改。用户可能会操纵其计算机上的 Cookie,这意味着会对安全性造成潜在风险或者导致依赖于 Cookie 的应用程序失败。另外,虽然 Cookie 只能被将它们发送到客户端的域访问,历史上黑客已经发现从用户计算机上的其他域访问 Cookie 的方法。您可以手动加密和解密 Cookie,但这需要额外的编码,并且因为加密和解密需要耗费一定的时间而影响应用程序的性能。

注意
Cookie 通常用于为已知用户自定义内容的个性化情况。在大多数此类情况中,Cookie 是作为“标识”而不是“身份验证”。因此,通常保护用于标识的 Cookie 的方法是在 Cookie 中存储用户名、帐户名或唯一用户 ID(例如 GUID),然后用以访问站点的用户个性化结构中的信息。

====================================================================

ASP.NET状态管理之二(查询字苻串QueryString)

查询字苻串是追加在URL后的数据(也是我常用的)

例如:
http://www.cnblogs.com/aierong/admin/EditPosts.aspx?opt=abc
这里?后的opt=1就是查询字符串

当我们在一页面向另一页面传递数据时可以用此方法。

使用如下方法接到数据:
string str=Request.QueryString[“opt“]
这样str就等于“abc“

这里我们传递的是英文字符,要是传递中文字符,我们得进行编码和解吗:
傳送時用Server.UrlEncode方法編碼,接收時用Server.UrlDecode解碼.

例如:
传递:
string url=“http://www.cnblogs.com/aierong/admin/EditPosts.aspx?opt=“+Server.UrlEncode(“我们“);
Response.Redirect(url);
接收:
string str=Server.UrlDecode(Request.QueryString[“opt“]);
这样str就等于“我们“

当然我们还有一省事的方法:

在web.config中修改globalization节为
<globalization
            requestEncoding="gb2312"
            responseEncoding="gb2312"
/>

总结

查询字符串是在页 URL 的结尾附加的信息。

可以使用查询字符串来通过 URL 将数据提交回您的页或另一页上。
查询字符串提供一种维护某些状态信息的简单但有限的方法。
例如,它们是将信息从一页传送到另一页的简便的方法(例如,将产品号传递到将处理该产品号的另一页)。

使用查询字符串的优点
1.不需要任何服务器资源:查询字符串包含在对特定 URL 的 HTTP 请求中。
2.广泛的支持:几乎所有的浏览器和客户端设备均支持使用查询字符串传递值。
3.实现简单:ASP.NET完全支持查询字符串方法,其中包含了使用 HttpRequest 对象的Params属性读取查询字符串的方法。

使用查询字符串的缺点
1.潜在的安全性风险  
用户可以通过浏览器用户界面直接看到查询字符串中的信息。
用户可将此URL设置为书签或发送给别的用户,从而通过此URL传递查询字符串中的信息。
如果您担心查询字符串中的任何敏感数据,请考虑使用窗体(使用 POST而不是查询字符串)中的隐藏域。

2.有限的容量  
有些浏览器和客户端设备对URL的长度有2083个字符的限制。

 

 

===============================

ASP.NET状态管理之八(应用程序Application)

 

ASP.NET 允许您使用应用程序状态来保存每个活动的 Web 应用程序的值,应用程序状态是 HttpApplicationState 类的一个实例。
应用程序状态是一种全局存储机制,可从 Web 应用程序中的所有页面访问。因此,应用程序状态可用于存储需要在服务器往返行程之间及页请求之间维护的信息。

应用程序状态存储在一个键/值字典中,在每次请求一个特定的 URL 期间就会创建这样一个字典。可以将特定于应用程序的信息添加到此结构以在页请求期间存储它。

一旦将应用程序特定的信息添加到应用程序状态中,服务器就会管理该对象。

应用程序状态是可用于 ASP.NET 应用程序中的所有类的数据储存库。
应用程序状态存储于服务器的内存中,并且比在数据库中存储和检索数据的速度更快。
与特定于单个用户会话的会话状态不同,应用程序状态应用于所有的用户和会话。
因此,应用程序状态非常适合存储那些数量少、不随用户的变化而变化的常用数据。

应用程序状态存储于 HttpApplicationState 类中,用户首次访问应用程序中的 URL 资源时将创建该类的新实例。
HttpApplicationState 类通过 Application 属性公开。

 

如何:从应用程序状态中读取值
应用程序状态存储类型化为 Object 的数据。因此,即使将数据存储于应用程序状态中时不必对其进行序列化,也必须在检索数据时将其强制转换为相应的类型。

确定应用程序变量是否存在,然后在访问该变量时将其转换为相应的类型。
下面的代码示例检索应用程序状态值 AppStartTime,并将其转换为一个 DateTime 类型的、名为 appStateTime 的变量。
if (Application["AppStartTime"] != null)
{
    DateTime myAppStartTime = (DateTime)Application["AppStartTime"];
}

 

如何:保存应用程序状态中的值
由于应用程序状态存储在服务器的内存中,因此应用程序状态中的大量数据可快速填充服务器内存。如果重新启动应用程序,应用程序状态数据便会丢失。应用程序状态无法在网络场中的多台服务器间或网络园中的辅助进程间共享。最后还请注意,应用程序状态采用自由线程模式,因此存储在应用程序状态中的任何数据必须具有内置的同步支持。

将值写入应用程序状态
在应用程序中设置 HttpApplicationState 类中变量的值。
下面的代码示例演示如何将应用程序变量 Message 设置为一个字符串。
Application["Message"] = "Welcome to the Contoso site.";

应用程序启动时将值写入应用程序状态
在应用程序 Global.asax 文件的 Application_Start 处理程序中,设置应用程序状态变量的值。同常规的 .aspx 页一样,HttpApplicationState 类也通过 Application 对象公开。
下面的代码示例演示如何将应用程序变量 Message 设置为一个字符串,并将变量 PageRequestCount 初始化为 0。
void Application_Start ( object sender , EventArgs e )
{
    // 在应用程序启动时运行的代码
    Application [ "Message" ] = "Welcome to the Contoso site.";
    Application [ "PageRequestCount" ] = 0;
}

用锁定方法将值写入应用程序状态
在设置应用程序变量的代码中,调用 System.Web.HttpApplicationState.Lock 方法,并设置应用程序状态值,然后调用 System.Web.HttpApplicationState.UnLock 方法取消锁定应用程序状态,释放应用程序状态以供其他写入请求使用。
下面的代码示例演示如何锁定和取消锁定应用程序状态。该代码将 PageRequestCount 变量值增加 1,然后取消锁定应用程序状态。
Application.Lock();
Application["PageRequestCount"] =  ((int)Application["PageRequestCount"])+1;
Application.UnLock();

应用程序状态注意事项
1.资源  
由于应用程序状态存储在内存中,因此比将数据保存到磁盘或数据库中速度更快。但是,在应用程序状态中存储较大的数据块可能会耗尽服务器内存,这会导致服务器将内存分页到磁盘。除了使用应用程序状态之外,还可以使用 ASP.NET 缓存机制来存储大量的应用程序数据。ASP.NET 缓存也是将数据存储在内存中,因此速度很快;但是,ASP.NET 会对缓存进行主动管理,如果内存不足时将移除项。
2.易失性  
由于应用程序状态存储在服务器内存中,因此每当停止或重新启动应用程序时应用程序状态都将丢失。例如,如果更改了 Web.config 文件,则要重新启动应用程序,此时除非将应用程序状态值写入非易失性存储媒体(如数据库)中,否则所有应用程序状态都将丢失。
3.可伸缩性  
应用程序状态不能在为同一应用程序服务的多个服务器间(如在网络场中)共享,也不能在同一服务器上为同一应用程序服务的多个辅助进程间(如在网络园中)共享。因此,应用程序不能依靠应用程序状态来实现在不同的服务器或进程间包含相同的应用程序状态数据。如果应用程序要在多处理器或多服务器环境中运行,可以考虑对必须在应用程序中准确保存的数据使用伸缩性更强的选项(如数据库)。
4.并发  
应用程序状态采用自由线程模式,即应用程序状态数据可由多个线程同时访问。因此,必须确保通过包含内置同步支持,以线程安全的方式进行应用程序状态数据更新。可以使用 Lock 和 UnLock 方法来确保数据的完整性,方法是锁定数据,使其一次只能由一个源进行写操作。还可以初始化 Global.asax 文件中 Application_Start 方法中的应用程序状态值,降低出现并发问题的可能性。

总结
ASP.NET 通过 HttpApplicationStatefrlrfSystemWebHttpApplicationStateClassTopic 类将应用程序状态提供为一种存储全局应用程序特定信息(对于整个应用程序都可见)的方法。应用程序状态变量实际上是 ASP.NET 应用程序的全局变量。
您可以在应用程序状态中存储应用程序特定的值,应用程序状态将由服务器来管理。
由多个会话共享并且不经常更改的数据是插入到应用程序状态变量的理想数据。

使用应用程序状态的优点
1.实现简单  
应用程序状态易于使用,为 ASP 开发人员所熟悉,并且与其他 .NET Framework 类一致。
2.应用程序范围  
由于应用程序状态可供应用程序中的所有页来访问,因此在应用程序状态中存储信息可能意味着仅保留信息的一个副本(例如,相对于在会话状态或在单独页中保存信息的多个副本)。

使用应用程序状态的缺点
1.应用程序范围  
应用程序状态的范围可能也是一项缺点。在应用程序状态中存储的变量仅对于该应用程序正在其中运行的特定进程而言是全局的,并且每一应用程序进程可能具有不同的值。因此,不能依赖应用程序状态来存储唯一值或更新 Web 场和 Web 园服务器配置中的全局计数器。
2.数据持续性有限  
因为在应用程序状态中存储的全局数据是易失的,所以如果包含这些数据的 Web 服务器进程被损坏(如因服务器崩溃、升级或关闭而损坏),将丢失这些数据。
3.资源要求  
应用程序状态需要服务器内存,这可能会影响服务器的性能以及应用程序的可伸缩性。

应用程序状态的精心设计和实现可以提高 Web 应用程序性能。例如,如果将常用的、相关的静态数据集放置到应用程序状态中,则可以通过减少对数据库的数据请求总数来提高站点性能。但是,这里存在一种性能平衡。当服务器负载增加时,包含大块信息的应用程序状态变量就会降低 Web 服务器的性能。在移除或替换值之前,将不释放在应用程序状态中存储的变量所占用的内存。因此,最好只将应用程序状态变量用于更改不频繁的小型数据集。

 =============================================

 

ASP.NET状态管理之四(暂存状态HttpContext.Items)

ASP.NET提供一个类System.Web.HttpContext ,用来表示上下文,此对象有一个属性Items

暂存状态就是利用HttpContext.Items属性来存放数据

MSDN中HttpContext.Items属性的解释是这样的:获取可用于在 HTTP 请求过程中在 IHttpModule 和 IHttpHandler 之间组织和共享数据的键值

HttpContext.Items属性中可以存放任何类型数据,无论这个属性中存放什么数据,都会在请求处理结束后自动清除,这就是暂存状态,数据的存放时间非常短.


//例如:
//我们有一页面A.ASPX,有一按钮ID:Submit,点按钮把页面转向b.aspx页面

public void Submit_Click(Object sender, EventArgs E)
{
    SqlConnection myConnection 
= new SqlConnection("server=(local)NetSDK;database=pubs;Trusted_Connection=yes");
    SqlDataAdapter myCommand 
= new SqlDataAdapter("select * from Authors", myConnection);
    DataSet ds 
= new DataSet();
    myCommand.Fill(ds, 
"Authors");
    
//把数据放入暂存中
    Context.Items["MyData"=ds;
    Server.Transfer(b.aspx);    
}



//b.aspx页面中

public void Page_Load(Object sender, EventArgs E)
  
{
    
if (!IsPostBack)
    
{
    
//取得暂存数据
    DataSet ds=(DataSet)Context.Items["MyData"];
    
//其它数据过程
    }

  }



在ibuyspyportal中我们也看到了此功能的使用:

查询字符串包含正被请求的选项卡的 TabIndedx 和 TabId 参数。在处理请求的整个过程中,一直使用此信息筛选要显示给用户的数据。
http://www.ibuyspyportal.com/DesktopDefault.aspx?tabindex=1&tabid=2
要使用查询字符串值,需要首先确保它是一个有效值,如果不是,则要进行一些错误处理。它并不是一大串代码,但是您真的要在每个使用该值的页和组件中复制它吗?当然不!在 Portal 示例中,甚至更多的地方都涉及到它,因为一旦我们知道了 TabId,就可以预先加载其他信息。

Portal 使用查询字符串值作为参数,以构造一个新的 PortalSettings 对象,并将它添加到 Global.asax 的 BeginRequest 事件的 Context.Items 中。由于在每个请求开始处都执行了开始请求,这使得与该选项卡有关的值在应用程序的所有页和组件中都可用。请求完成后,对象将被自动丢弃


void Application_BeginRequest(Object sender, EventArgs e)
 

     
  
int tabIndex = 0
  
int tabId = 0

  
// 从查询字符串获取 TabIndex 

  
if (Request.Params["tabindex"!= null{        
    tabIndex 
= Int32.Parse(Request.Params["tabindex"]); 
  }
 
         
  
// 从查询字符串获取 TabID 

  
if (Request.Params["tabid"!= null{        
    tabId 
= Int32.Parse(Request.Params["tabid"]); 
  }
 

  Context.Items.Add(
"PortalSettings"new PortalSettings(tabIndex, tabId)); 
}
 



DesktopPortalBanner.ascx 用户控件从 Context 请求 PortalSetting 的对象,以访问 Portal 的名称和安全设置。事实上,此模块是操作中的 Context 的一个典型综合示例。为阐明这一点,我已将代码进行了一些简化,并用粗体标记了 HTTP 或应用程序特定的 Context 被访问过的所有地方。

<%@ Import Namespace="ASPNetPortal" %> 
<%@ Import Namespace="System.Data.SqlClient" %> 

<script language="C#" runat="server"> 

  public 
int     tabIndex; 
  public bool     ShowTabs 
= true
  protected String  LogoffLink 
= ""

  
void Page_Load(Object sender, EventArgs e) 

    
// 从当前上下文获取 PortalSettings 
 PortalSettings portalSettings = 
(PortalSettings) Context.Items[
"PortalSettings"]; 

    
// 动态填充门户站点名称 
    siteName.Text = portalSettings.PortalName; 

    
// 如果用户已登录,自定义欢迎信息 
    if (Request.IsAuthenticated == true
     
      WelcomeMessage.Text 
= "欢迎" + 
Context.User.Identity.Name 
+ "!<" + 
"span class=Accent" + ">|<" + "/span" + ">"

      
// 如果身份验证模式为 Cookie,则提供一个注销链接 
      if (Context.User.Identity.AuthenticationType == "Forms"
        LogoffLink 
= "<" + "span class="Accent">|</span> " + 
"<a href=" + Request.ApplicationPath + 
"/Admin/Logoff.aspx class=SiteLink> 注销" + 
"</a>"
      }
 
    }
 

    
// 动态显示门户选项卡条 
    if (ShowTabs == true

      tabIndex 
= portalSettings.ActiveTab.TabIndex; 

      
// 生成要向用户显示的选项卡列表                  
      ArrayList authorizedTabs = new ArrayList(); 
      
int addedTabs = 0

      
for (int i=0; i < portalSettings.DesktopTabs.Count; i++
       
        TabStripDetails tab 
= 
(TabStripDetails)portalSettings.DesktopTabs[i]; 

        
if (PortalSecurity.IsInRoles(tab.AuthorizedRoles)) 
          authorizedTabs.Add(tab); 
        }
 

        
if (addedTabs == tabIndex) 
          tabs.SelectedIndex 
= addedTabs; 
        }
 

        addedTabs
++
      }
      

      
// 用已授权的选项卡填充页顶部的选项卡 
//
 列表 
      tabs.DataSource = authorizedTabs; 
      tabs.DataBind(); 
    }
 
  }
 

</script> 
<table width="100%" cellspacing="0" class="HeadBg" border="0"> 
  
<tr valign="top"> 
    
<td colspan="3" align="right"> 
      
<asp:label id="WelcomeMessage" runat="server" /> 
      
<href="<%= Request.ApplicationPath %>">Portal 主页</a> 
<span class="Accent"> |</span> 
<href="<%= Request.ApplicationPath %>/Docs/Docs.htm"> 
        Portal 文档
</a> 
      
<%= LogoffLink %> 
         
    
</td> 
  
</tr> 
  
<tr> 
    
<td width="10" rowspan="2"> 
        
    
</td> 
    
<td height="40"> 
      
<asp:label id="siteName" runat="server" /> 
    
</td> 
    
<td align="center" rowspan="2"> 
     
    
</td> 
  
</tr> 
  
<tr> 
    
<td> 
      
<asp:datalist id="tabs" runat="server"> 
        
<ItemTemplate> 
           
<href='<%= Request.ApplicationPath %> 
/DesktopDefault.aspx?tabindex=
<%# Container.ItemIndex %>&tabid
<%# ((TabStripDetails) Container.DataItem).TabId %>'> 
<%# ((TabStripDetails) Container.DataItem).TabName %> 
</a>  
        
</ItemTemplate> 
        
<SelectedItemTemplate> 
           
         
<span class="SelectedTab"> 
<%# ((TabStripDetails) Container.DataItem).TabName %> 
</span>  
        
</SelectedItemTemplate> 
      
</asp:datalist> 
    
</td> 
  
</tr> 
</table> 

 

 ==============================================

 

ASP.NET状态管理之九(会话Session)

ASP.NET 允许您使用会话状态保存每个活动的 Web 应用程序会话的值,会话状态是 HttpSessionState 类的一个实例。

会话状态与应用程序状态相似,不同的只是会话状态的范围限于当前的浏览器会话。如果有不同的用户在使用您的应用程序,则每个用户会话都将有一个不同的会话状态。此外,如果同一用户在退出后又返回到应用程序,第二个用户会话的会话状态也会与第一个不同。

会话状态采用键/值字典形式的结构来存储特定于会话的信息,这些信息需要在服务器往返行程之间及页请求之间进行维护。

可以使用会话状态来完成以下任务:
1.唯一标识浏览器或客户端设备请求,并将这些请求映射到服务器上的单独会话实例。
2.在服务器上存储特定于会话的数据,以用于同一个会话内的多个浏览器或客户端设备请求。
3.引发适当的会话管理事件。此外,可以利用这些事件编写应用程序代码。

一旦将应用程序特定的信息添加到会话状态中,服务器就会管理该对象。根据您指定的选项的不同,可以将会话信息存储在 Cookie 中、进程外服务器中或运行 Microsoft SQL Server 的计算机中。

默认情况下,所有 ASP.NET 应用程序都启用 ASP.NET 会话状态。

 

会话变量
如何:保存会话状态中的值
会话变量存储在通过 System.Web.HttpContext.Session 属性公开的 SessionStateItemCollection 中。
会话变量集合按变量名称或整数索引来进行索引。仅需通过名称引用会话变量即可创建会话变量。无需声明会话变量或将会话变量显式添加到集合中。
例如,下面的代码示例创建分别表示用户的名字和姓氏的会话变量,并将它们设置为从 TextBox 控件检索到的值。
Session["FirstName"] = FirstNameTextBox.Text;
Session["LastName"] = LastNameTextBox.Text;

默认情况下,会话变量可以为任何有效的 .NET 类型。
例如,下面的代码示例将值的 ArrayList 存储在名为“StockPicks”的会话变量中。注意,由“StockPicks”会话变量返回的值在从 SessionStateItemCollection 检索出来时必须强制转换为适当的类型。
ArrayList stockPicks = (ArrayList)Session["StockPicks"];

Session["StockPicks"] = stockPicks;

如何:读取会话状态中的值
此示例访问 Item 属性来检索会话状态中的值。
string firstName = (string)(Session["First"]);
string lastName = (string)(Session["Last"]);
string city = (string)(Session["City"]);

如果尝试从不存在的会话状态中获取值,则不会引发任何异常。若要确保所需的值在会话状态中,请首先使用测试(例如以下测试)检查该对象是否存在:
if (Session["City"] == null)
    // No such value in session state; take appropriate action.

 

会话标识符
浏览器的会话使用存储在 SessionID 属性中的唯一标识符进行标识。会话 ID 使 ASP.NET 应用程序能够将特定的浏览器与 Web 服务器上相关的会话数据和信息相关联。会话 ID 的值在浏览器和 Web 服务器间通过 Cookie 进行传输,如果指定了无 Cookie 会话,则通过 URL 进行传输。

无 Cookie SessionID
默认情况下,SessionID 存储在浏览器未到期会话的 Cookie 中。通过在 Web.config 文件的 sessionState 节中将 cookieless 属性设置为 TRUE,可以指定不将会话标识符存储在 Cookie 中。
ASP.NET 通过自动在页的 URL 中插入唯一的会话 ID 来保持无 Cookie 会话状态。
例如,下面的 URL 已被 ASP.NET 修改,以包含唯一的会话 ID lit3py55t21z5v55vlm25s55:
http://www.example.com/s(lit3py55t21z5v55vlm25s55)/orderform.aspx

ASP.NET 修改所有请求页中包含的使用相对于应用程序的路径的链接(不修改显式路径),方法是在将每页发送到浏览器之前,在链接中嵌入一个会话 ID 值。只要用户遵循 ASP.NET 应用程序提供的链接路径,即可保持会话状态。但是,如果客户端重写了应用程序提供的 URL,ASP.NET 将不能解析此会话 ID,也不能将此请求与现有的会话相关联,因此会为此请求启动一个新的会话。

会话 ID 嵌入在 URL 中应用程序名称后的斜杠之后,在其余所有文件或虚拟目录标识符之前。

 

会话状态事件
ASP.NET 提供了两种帮助您管理用户会话的事件:Session_OnStart 事件和 Session_OnEnd 事件;前者在新会话开始时引发,后者在会话被放弃或过期时引发。

Session_OnStart 事件
可以通过向 Global.asax 文件添加一个名为 Session_OnStart 的子例程来处理 Session_OnStart 事件。如果请求开始一个新会话,Session_OnStart 子例程会在请求开始时运行。如果请求不包含 SessionID 值或请求所包含的 SessionID 属性引用一个已过期的会话,则会开始一个新会话。
您可以使用 Session_OnStart 事件初始化会话变量并跟踪与会话相关的信息。

Session_OnEnd 事件
可以通过向 Global.asax 文件添加一个名为 Session_OnEnd 的子例程来处理 Session_OnEnd 事件。Session_OnEnd 子例程在 Abandon 方法已被调用或会话已过期时运行。如果超过了某一会话 Timeout 属性指定的分钟数并且在此期间内没有请求该会话,则该会话过期。
只有会话状态属性 Mode 设置为 InProc(默认值)时,才支持 Session_OnEnd 事件。如果会话状态属性 Mode 为 StateServer 或 SQLServer,则忽略 Global.asax 文件中的 Session_OnEnd 事件。如果会话状态属性 Mode 设置为 Custom,则由自定义会话状态存储提供程序决定是否支持 Session_OnEnd 事件。
您可以使用 Session_OnEnd 事件清除与会话相关的信息,如由 SessionID 值跟踪的数据源中的用户信息。

下面的代码示例演示一个可以添加到 Global.asax 文件的 Session_OnStart 和 Session_OnEnd 子例程示例。在此示例中定义的这些子例程创建一个计数器,用来跟踪正在使用应用程序的应用程序用户的数量。请注意,只有会话状态属性 Mode 设置为 InProc 时,此示例才会正常运行,因为只有进程内会话状态存储才支持 Session_OnEnd 事件。
<script language="C#" runat="server">
public void Application_OnStart()
{
  Application["UsersOnline"] = 0;
}

public void Session_OnStart()
{
  Application.Lock();
  Application["UsersOnline"] = (int)Application["UsersOnline"] + 1;
  Application.UnLock();
}

public void Session_OnEnd()
{
  Application.Lock();
  Application["UsersOnline"] = (int)Application["UsersOnline"] - 1;
  Application.UnLock();
}
</script>

 

会话状态模式
ASP.NET 会话状态支持若干用于会话数据的存储选项。每个选项都由 SessionStateMode 枚举中的一个值标识。
下面的列表描述了可用的会话状态模式:
InProc 模式,此模式将会话状态存储在 Web 服务器上的内存中。这是默认设置。
StateServer 模式,此模式将会话状态存储在一个名为 ASP.NET 状态服务的单独进程中。这确保了在重新启动 Web 应用程序时会保留会话状态,并让会话状态可用于网络场中的多个 Web 服务器。
SQLServer 模式将会话状态存储到一个 SQL Server 数据库中。这确保了在重新启动 Web 应用程序时会保留会话状态,并让会话状态可用于网络场中的多个 Web 服务器。
Custom 模式,此模式允许您指定自定义存储提供程序。
Off 模式,此模式禁用会话状态。
通过在应用程序的 Web.config 文件中为 sessionState 元素的 mode 属性分配一个 SessionStateMode 枚举值,可以指定要让 ASP.NET 会话状态使用的模式。除了 InProc 和 Off 之外,其他模式都需要附加参数,例如将在本主题后面讨论的连接字符串值。通过访问 System.Web.SessionState.HttpSessionState.Mode 属性的值,可以查看当前选定的会话状态。

进程内模式
进程内模式是默认的会话状态模式,使用 InProcSessionStateMode 枚举值指定。进程内模式将会话状态值和变量存储在本地 Web 服务器上的内存中。它是唯一支持 Session_OnEnd 事件的模式。

状态服务器模式
StateServer 模式将会话状态存储在一个称为 ASP.NET 状态服务的进程中,该进程是独立于 ASP.NET 辅助进程或 IIS 应用程序池的单独进程。使用此模式可以确保在重新启动 Web 应用程序时保留会话状态,并使会话状态可用于网络场中的多个 Web 服务器。

SQL Server 模式
SQLServer 模式将会话状态存储到一个 SQL Server 数据库中。使用此模式可以确保在重新启动 Web 应用程序时保留会话状态,并使会话状态可用于网络场中的多个 Web 服务器。

自定义模式
Custom 模式指定您希望使用自定义会话状态存储提供程序来存储会话状态数据。在使用 Custom 的 Mode 配置 ASP.NET 应用程序时,必须使用 sessionState 配置元素的 providers 子元素指定会话状态存储提供程序的类型。使用 add 子元素来指定提供程序类型,并包括指定提供程序类型名称的 type 属性以及指定提供程序实例名称的 name 属性。然后,将提供程序实例的名称提供给 sessionState 元素的 customProvider 属性,将 ASP.NET 会话状态配置为使用该提供程序实例来存储和检索会话数据。

 

配置会话状态
使用 system.web 配置节的 sessionState 元素来配置会话状态。还可以使用 EnableSessionState 页指令来配置会话状态。

使用 sessionState 元素可以指定会话存储数据的模式、在客户端和服务器间发送会话标识符值的方式、会话 Timeout 值和基于会话 Mode 的支持值。例如,下面的 sessionState 元素将应用程序配置为 SQLServer 会话模式,Timeout 为 30 分钟,并指定将会话标识符存储在 URL 中。

<sessionState mode="SQLServer"
  cookieless="true "
  regenerateExpiredSessionId="true "
  timeout="30"
  sqlConnectionString="Data Source=MySqlServer;Integrated Security=SSPI;"
  stateNetworkTimeout="30"/>

可以通过将会话状态模式设置为 Off 来禁用应用程序的会话状态。如果只希望禁用应用程序的某个特定页的会话状态,则可以将 EnableSessionState 页指令设置为 false。注意,还可将 EnableSessionState 页指令设置为 ReadOnly 以提供对会话变量的只读访问。

 

并发请求和会话状态
对 ASP.NET 会话状态的访问专属于每个会话,这意味着如果两个不同的用户同时发送请求,则会同时授予对每个单独会话的访问。但是,如果这两个并发请求是针对同一会话的(即,使用相同的 SessionID 值),则接收到的第一个请求获得对会话信息的独占访问权,而第二个请求将在第一个请求完成之后执行,或直到由于第一个请求超过锁定超时时间而释放对该信息的独占锁定后再执行。如果将 EnableSessionState 页指令设置为 ReadOnly,则对只读会话信息的请求不会导致对会话数据的独占锁定。可能仍需等到会话数据由读写请求而获得的锁定被解除后,对会话数据的只读请求才能得到满足。

 

 

==========================================================

ASP.NET状态管理之六(缓存Cache)

ASP.NET 为您提供了一个强大的、便于使用的缓存机制,用于将需要大量服务器资源来创建的对象存储在内存中。缓存这些类型的资源会大大改进应用程序的性能。

缓存是由 Cache 类实现的;缓存实例是每个应用程序专用的。缓存生存期依赖于应用程序的生存期;重新启动应用程序后,将重新创建 Cache 对象。

设计 Cache 类是为了便于使用。您可以将项放置在 Cache 中,并在以后使用简单的键/值对来检索这些项。

Cache 类提供了强大的功能,允许您自定义如何缓存项以及将它们缓存多长时间。例如,当缺乏系统内存时,缓存会自动移除很少使用的或优先级较低的项以释放内存。该技术也称为清理,这是缓存确保过期数据不使用宝贵的服务器资源的方式之一。

当执行清理时,您可以指示 Cache 给予某些项比其他项更高的优先级。若要指示项的重要性,可以在使用 Add 或 Insert 方法添加项时指定一个 CacheItemPriority 枚举值。

当使用 Add 或 Insert 方法将项添加到缓存时,您还可以建立项的过期策略。您可以通过使用 DateTime 值指定项的确切过期时间(绝对过期时间),来定义项的生存期。也可以使用 TimeSpan 值指定一个弹性过期时间,弹性过期时间允许您根据项的上次访问时间来指定该项过期之前的运行时间。一旦项过期,便将它从缓存中移除。试图检索它的值的行为将返回 null(在 Visual Basic 中为 Nothing),除非该项被重新添加到缓存中。

对于存储在缓存中的易失项(例如那些定期进行数据刷新的项或那些只在一段时间内有效的项),通常设置一种过期策略:只要这些项的数据保持为最新的,就将它们保留在缓存中。例如,如果您正在编写一个应用程序,该应用程序通过另一个网站获取数据来跟踪体育比赛的比分,那么只要源网站上比赛的比分不更改,就可以缓存这些比分。在此情况下,您可以根据其他网站更新比分的频率来设置过期策略。您可以编写代码来确定缓存中是否是最新的比分。如果该比分不是最新的,则代码可以从源网站读取比分并缓存新值。

最后,ASP.NET 允许您根据外部文件、目录(文件依赖项)或另一个缓存项(键依赖项)来定义缓存项的有效性。如果具有关联依赖项的项发生更改,缓存项便会失效并从缓存中移除。您可以使用该技术在项的数据源更改时从缓存中移除这些项。例如,如果您编写一个处理 XML 文件中的财务数据的应用程序,则可以从该文件将数据插入缓存中并在此 XML 文件上保留一个依赖项。当该文件更新时,从缓存中移除该项,您的应用程序重新读取 XML 文件,然后将刷新后的数据放入缓存中。

 

将项添加到缓存中
可以使用 Cache 对象访问应用程序缓存中的项。可以使用 Cache 对象的 Insert 方法向应用程序缓存添加项。该方法向缓存添加项,并且通过几次重载,您可以用不同选项添加项,以设置依赖项、过期和移除通知。如果使用 Insert 方法向缓存添加项,并且已经存在与现有项同名的项,则缓存中的现有项将被替换。

还可以使用 Add 方法向缓存添加项。使用此方法,您可以设置与 Insert 方法相同的所有选项;然而,Add 方法将返回您添加到缓存中的对象。另外,如果使用 Add 方法,并且缓存中已经存在与现有项同名的项,则该方法不会替换该项,并且不会引发异常。

通过键和值直接设置项向缓存添加项
通过指定项的键和值,像将项添加到字典中一样将其添加到缓存中。
下面的代码示例将名为 CacheItem1 的项添加到 Cache 对象中:
Cache["CacheItem1"] = "Cached Item 1";

通过使用 Insert 方法将项添加到缓存中
调用 Insert 方法,传递要添加的项的键和值。
下面的代码示例添加名为 CacheItem2 的字符串:
Cache.Insert("CacheItem2", "Cached Item 2");

通过指定依赖项向缓存添加项
调用 Insert 方法,将 CacheDependency 对象的一个实例传递给该方法
下面的代码示例添加名为 CacheItem3 的项,该项依赖于缓存中名为 CacheItem2 的另一个项:
string[] dependencies = { "CacheItem2" };
Cache.Insert("CacheItem3", "Cached Item 3",
    new System.Web.Caching.CacheDependency(null, dependencies));

下面的代码示例演示将名为 CacheItem4 的项添加到缓存中,并且在名为 XMLFile.xml 的文件上设置文件依赖项:
Cache.Insert("CacheItem4", "Cached Item 4",
    new System.Web.Caching.CacheDependency(
    Server.MapPath("XMLFile.xml")));

下面的代码示例演示如何创建多个依赖项。它向缓存中名为 CacheItem1 的另一个项添加键依赖项,向名为 XMLFile.xml 的文件添加文件依赖项。
System.Web.Caching.CacheDependency dep1 =
    new System.Web.Caching.CacheDependency(Server.MapPath("XMLFile.xml"));
string[] keyDependencies2 = { "CacheItem1" };
System.Web.Caching.CacheDependency dep2 =
    new System.Web.Caching.CacheDependency(null, keyDependencies2);
System.Web.Caching.AggregateCacheDependency aggDep =
    new System.Web.Caching.AggregateCacheDependency();
aggDep.Add(dep1);
aggDep.Add(dep2);
Cache.Insert("CacheItem5", "Cached Item 5", aggDep);

将设有过期策略的项添加到缓存中
调用 Insert 方法,将绝对过期时间或弹性过期时间传递给该方法。
下面的代码示例将有一分钟绝对过期时间的项添加到缓存中:
Cache.Insert("CacheItem6", "Cached Item 6",
    null, DateTime.Now.AddMinutes(1d),
    System.Web.Caching.Cache.NoSlidingExpiration);

下面的代码示例将有 10 分钟弹性过期时间的项添加到缓存中:
Cache.Insert("CacheItem7", "Cached Item 7",
    null, System.Web.Caching.Cache.NoAbsoluteExpiration,
    new TimeSpan(0, 10, 0));

将设有优先级设置的项添加到缓存中
调用 Insert 方法,从 CacheItemPriority 枚举中指定一个值。
下面的代码示例将优先级值为 High 的项添加到缓存中:
Cache.Insert("CacheItem8", "Cached Item 8",
    null, System.Web.Caching.Cache.NoAbsoluteExpiration,
    System.Web.Caching.Cache.NoSlidingExpiration,
    System.Web.Caching.CacheItemPriority.High, null);

使用 Add 方法向缓存添加项
调用 Add 方法,它返回一个表示项的对象。
下面的代码示例向缓存添加名为 CacheItem9 的项,同时将变量 CachedItem9 的值设置为已添加的项。
string CachedItem9 = (string)Cache.Add("CacheItem9",
    "Cached Item 9", null,
    System.Web.Caching.Cache.NoAbsoluteExpiration,
    System.Web.Caching.Cache.NoSlidingExpiration,
    System.Web.Caching.CacheItemPriority.Default,
    null);


缓存依赖例题:
我们在一页面建立1个按钮,查看CACHE是否存在
在窗体启动时我们创建CACHE,名称="txt2",数值=数据集ds
该CACHE与myfile.xml相关联,当myfile.xml文件变化时,txt2CACHE就被自动删除

private void Page_Load(object sender, System.EventArgs e)
  {
   if( !IsPostBack  )
   {
   string FilePath=MapPath("myfile.xml");
   SqlConnection con=new SqlConnection("Uid=sa;database=pubs;");
   SqlDataAdapter da =new SqlDataAdapter("select * from authors",con);
   DataSet ds=new DataSet();
   da.Fill(ds);
   System.Web.Caching.CacheDependency CacheDependencyXmlFile=new System.Web.Caching.CacheDependency(FilePath);
   Cache.Insert("txt2",ds ,CacheDependencyXmlFile);
   }
  }


为了监视pubs数据库authors表的变化
我们可以在pubs数据库authors表建立触发器
一旦authors表中发生增加,删除,修改数据时,触发器会自动修改文件myfile.xml
一旦myfile.xml文件变化,txt2CACHE就被系统自动删除

CREATE TRIGGER tr_authors
ON authors
FOR INSERT, UPDATE ,delete
AS
declare @cmd nvarchar(4000)
select @cmd='bcp "select convert(nvarchar(30),Getdate(),13)" queryout D:\cache\WebCache\myfile.xml -c -Sglc2403 -Usa -P'
exec master..xp_cmdshell @cmd
GO


private void QueryButton_Click(object sender, System.EventArgs e)
{
if ( Cache["txt2"]!=null)
{
 Response.Write("exists");
}
else
{
 Response.Write("not exists");
}
}

首先我们点按钮,显示Cache["txt2"]存在
现在我们去表authors中任意修改一数据,再点按钮,显示Cache["txt2"]不存在拉


以上我们是把CACHE是和一个文件相关联,我们还可以把CACHE和文件组关联,建立依赖
以下我们把CACHE和2个文件(myfile.xml,myfile1.xml)关联,一旦文件组中其中任意一文件变化,Cache会把"txt2"项的数据从CACHE中删除

string[] FilePath=new String[]{MapPath("myfile.xml"),MapPath("myfile1.xml")};
System.Web.Caching.CacheDependency CacheDependencyXmlFile=new                     System.Web.Caching.CacheDependency(FilePath);
string CacheVaule="a";
Cache.Insert("txt2", CacheVaule ,CacheDependencyXmlFile);

 


缓存依赖可以是文件,还可以是其他对象的键
下面的代码说明缓存Cache["txt2"]既依赖文件myfile.xml,又依赖缓存中的Cache["txt"],只要这2者任意一样改变,缓存Cache["txt2"]就会清除

Cache["txt"] = "b";
string[] FilePath=new String[]{ MapPath("myfile.xml")};
string[] dependencyKey=new String[]{"txt"};
SqlConnection con=new SqlConnection("Uid=sa;database=pubs;");
SqlDataAdapter da =new SqlDataAdapter("select * from authors",con);
DataSet ds=new DataSet();
da.Fill(ds);
System.Web.Caching.CacheDependency CacheDependencyXmlFile=
          new System.Web.Caching.CacheDependency(FilePath,dependencyKey);
Cache.Insert("txt2",ds ,CacheDependencyXmlFile);

 

检索缓存项的值
要从缓存中检索数据,应指定存储缓存项的键。不过,由于缓存中所存储的信息为易失信息,即该信息可能由 ASP.NET 移除,因此建议的开发模式是首先确定该项是否在缓存中。如果不在,则应将它重新添加到缓存中,然后检索该项。

通过在 Cache 对象中进行检查来确定该项是否不为 null(在 Visual Basic 中为 Nothing)。如果该项存在,则将它分配给变量。否则,重新创建该项,将它添加到缓存中,然后访问它。
下面的代码示例演示如何确定名为 CacheItem 的项是否包含在缓存中。如果在,则代码会将该项的内容分配给名为 cachedString 的变量。如果该项不在缓存中,则代码会将它添加到缓存中,然后将它分配给 cachedString。
string cachedString;
if (Cache["CacheItem"] != null)
{
    cachedString = (string)Cache["CacheItem"];
}
else
{
    Cache.Insert("CacheItem", "Hello, World.");
    cachedString = (string)Cache["CacheItem"];
}

 

从 ASP.NET 缓存中删除项
ASP.NET 缓存中的数据是易失的,即不能永久保存。由于以下任一原因,缓存中的数据可能会自动移除:
1.缓存已满。
2.该项已过期。
3.依赖项发生更改。

从缓存中显式删除项
调用 Remove 方法,以传递要移除的项的键。
下面的示例演示如何移除键为 MyData1 的项。
Cache.Remove("MyData1");



从缓存中移除项时通知应用程序
在大多数缓存方案中,当从缓存中移除项后,直到再次需要此项时,才需要将其放回缓存中。典型的开发模式是在使用项之前始终检查该项是否已在缓存中。如果项位于缓存中,则可以使用。如果不在缓存中,则应再次检索该项,然后将其添加回缓存。

但是,在某些情况下,如果从缓存中移除项时通知应用程序,可能非常有用。例如,您可能具有一个缓存的报告,创建该报告需花费大量的时间进行处理。当该报告从缓存中移除时,您希望重新生成该报告,并立即将其置于缓存中,以便下次请求该报告时,用户不必等待对此报告进行处理。

为了在从缓存中移除项时能够发出通知,ASP.NET 提供了 CacheItemRemovedCallback 委托。该委托定义编写事件处理程序时使用的签名,当对从缓存中移除项进行响应时会调用此事件处理程序。ASP.NET 还提供 CacheItemRemovedReason 枚举,用于指定移除缓存项的原因。

通常,通过在管理尝试检索的特定缓存数据的业务对象中创建处理程序,来实现回调。例如,您可能有一个 ReportManager 对象,该对象具有两种方法,即 GetReport 和 CacheReport。GetReport 报告方法检查缓存以查看报告是否已缓存;如果没有,该方法将重新生成报告并将其缓存。CacheReport 方法具有与 CacheItemRemovedCallback 委托相同的函数签名;从缓存中移除报告时,ASP.NET 会调用 CacheReport 方法,然后将报告重新添加到缓存中。

当从缓存中移除项时通知应用程序
1.创建一个类,负责从缓存中检索项并处理回调方法,以将项添加回缓存中。
2.在该类中,创建用于将项添加到缓存中的方法。
3.在该类中,创建用于从缓存中获取项的方法。
4.创建用于处理缓存项移除回调的方法。该方法必须具备与 CacheItemRemovedCallback 委托相同的函数签名。从缓存中删除项时,会在该方法中执行要运行的逻辑,如重新生成项并将其添加回缓存中。


static System.Web.Caching.CacheItemRemovedReason reason;
System.Web.Caching.CacheItemRemovedCallback onRemove = null;

public void RemovedCallback(String k, Object v, System.Web.Caching.CacheItemRemovedReason r)
{
 itemRemoved = true;
 reason = r;
}

onRemove = new System.Web.Caching.CacheItemRemovedCallback (this.RemovedCallback);
Cache.Insert("txt",ds,null,
System.Web.Caching.Cache.NoAbsoluteExpiration,
System.Web.Caching.Cache.NoSlidingExpiration,
System.Web.Caching.CacheItemPriority.Default,onRemove);

 

由于任何原因从Cache中移除时,将调用 RemovedCallback 方法

===================================

ASP.NET状态管理之十(配置文件Profile)

在许多应用程序中,需要存储并使用对用户唯一的信息。用户访问站点时,您可以使用已存储的信息向用户显示 Web 应用程序的个性化版本。个性化应用程序需要大量的元素:必须使用唯一的用户标识符存储信息,能够在用户再次访问时识别用户,然后根据需要获取用户信息。若要简化应用程序,可以使用 ASP.NET 配置文件功能,该功能可为您执行所有上述任务。

ASP.NET 配置文件功能将信息与单个用户关联,并采用持久性的格式存储这些信息。通过配置文件,可以管理用户信息,而无需创建和维护自己的数据库。此外,通过使用可从应用程序中的任何位置访问的强类型 API,就可以借助 ASP.NET 配置文件功能使用相关的用户信息。

可以使用配置文件存储任何类型的对象。配置文件功能提供了一项通用存储功能,使您能够定义和维护几乎任何类型的数据,同时仍可用类型安全的方式使用数据。

ASP.NET 配置文件的工作方式
若要使用配置文件,首先应通过修改 ASP.NET Web 应用程序的配置文件来启用配置文件。要在配置中指定配置文件提供程序,该提供程序是执行存储和检索配置文件数据等低级任务的基础类。可以使用 .NET Framework 中包括的配置文件提供程序(会将配置文件数据存储在 SQL Server 中),也可以按照实现配置文件提供程序主题中描述的那样创建并使用自己的配置文件提供程序。可以指定连接到所选的数据库的 SqlProfileProvider 实例,也可以使用将配置文件数据存储在本地 Web 服务器上的默认 SqlProfileProvider 实例。

通过定义要维护其值的属性的列表,可以对配置文件功能进行配置。例如,您可能需要存储用户的邮政编码,以使您的应用程序可以提供特定于区域的信息,如天气预报等。在配置文件中,应定义一个名为 PostalCode 的配置文件属性。配置文件的 profile 节与如下所示类似:
<profile>
  <properties>
    <add name="PostalCode" />
  </properties>
</profile>

应用程序运行时,ASP.NET 会创建一个 ProfileCommon 类,该类是一个动态生成的类,从 ProfileBase 类继承而来。动态的 ProfileCommon 类包括根据在应用程序配置中指定的配置文件属性定义创建的属性。然后,会将此动态 ProfileCommon 类的实例设置为当前 HttpContext 的 Profile 属性的值,并且可在应用程序的页中使用。

在应用程序中,可以收集要存储的值,并将其赋值给已定义的配置文件属性。例如,应用程序的主页可能包含提示用户输入邮政编码的文本框。用户输入邮政编码时,可以设置 Profile 属性,以存储当前用户的值,如下面的示例所示:
Profile.PostalCode = txtPostalCode.Text;

为 Profile.PostalCode 设置值时,会自动为当前用户存储该值。无需编写任何代码确定当前用户的身份,也无需将值显式地存储于数据库中,该配置文件功能将为您执行这些任务。

如果要使用该值,可以采用与设置该值基本相同的方法获取该值。例如,下面的代码示例演示如何调用名为 GetWeatherInfo 的假设函数,从而将其传递给配置文件中存储的当前用户的邮政编码:
weatherInfo = GetWeatherInfo( Profile.PostalCode );

无需显式确定用户身份或执行任何数据库查找。只需从配置文件中获取属性值,便使 ASP.NET 可以执行必要的操作,从而标识当前用户并查找持久性配置文件存储区中的值。


具体参考
http://msdn.microsoft.com/en-us/library/ms379605.aspx

==============================

ASP.NET状态管理之十三(总结)

状态管理是您对同一页或不同页的多个请求维护状态和页信息的过程。与所有基于 HTTP 的技术一样,Web 窗体页是无状态的,这意味着它们不自动指示序列中的请求是否全部来自相同的客户端,或者单个浏览器实例是否一直在查看页或站点。此外,到服务器的每一往返过程都将销毁并重新创建页;因此,如果超出了单个页的生命周期,页信息将不存在。

ASP.NET 提供多种方式来在服务器往返过程之间维护状态。对这些状态管理选项的选择主要取决于您的应用程序,并且应基于以下条件:
需要存储的信息量有多大?
客户端是接受持久性的还是内存中的 Cookie?
要将信息存储在客户端还是服务器上?
信息是否是敏感信息?
您对应用程序设定了什么样的性能和带宽条件?
目标浏览器和设备具有什么样的功能?
您是否需要储存基于用户的信息?
信息需要存储多长时间?
您使用 Web 场(多个服务器)、Web 园(一个计算机上的多个进程)还是单个进程来运行应用程序?

 

客户端状态管理
视图状态

Web 窗体页提供 ViewState 属性作为内置结构,在对同一页的多个请求间自动保留值。视图状态作为页中的隐藏域来进行维护。

可以使用视图状态在页回发到自身时跨越往返过程存储您自己的页特定值。例如,如果您的应用程序正在维护用户特定的信息(即,该信息在页上使用,但不是任何控件所必需的部分),则可以使用视图状态存储该信息。

使用视图状态的优点
不需要任何服务器资源   视图状态包含在页代码内的结构中。
实现简单   视图状态无需使用任何自定义编程。默认情况下对控件启用状态数据的维护。
增强的安全功能   视图状态中的值经过哈希计算和压缩,并且针对 Unicode 实现进行编码,其安全性要高于使用隐藏域。

使用视图状态的缺点
性能注意事项   由于视图状态存储在页本身,因此如果存储较大的值,用户显示页和发送页时的速度可能会减慢。尤其是对移动设备,其带宽通常是有限的。
设备限制   移动设备可能没有足够的内存容量来存储大量的视图状态数据。
潜在的安全风险   视图状态存储在页上的一个或多个隐藏域中。虽然视图状态以哈希格式存储数据,但它可以被篡改。如果直接查看页输出源,可以看到隐藏域中的信息,这导致潜在的安全性问题。

 


控件状态
ASP.NET 页框架提供了 ControlState 属性作为在服务器往返过程中存储自定义控件数据的方法。例如,如果您编写的自定义控件使用多个不同的选项卡来显示不同的信息,为使此控件能够按预期方式工作,控件需要知道在往返过程中哪个选项卡被选中。视图状态可用于此目的,但是开发人员可能在页级将视图状态关闭,这实际上破坏了您的控件。与视图状态不同,控件状态不能被关闭,因此它提供了存储控件状态数据的更可靠方法。

使用控件状态的优点
不需要任何服务器资源   默认情况下,控件状态存储在页上的隐藏域中。
可靠性   因为控件状态不像视图状态那样可以关闭,控件状态是管理控件的状态的更可靠方法。
通用性   可以编写自定义适配器来控制如何存储控件状态数据和控件状态数据的存储位置。

使用控件状态的缺点
需要一些编程   虽然 ASP.NET 页框架为控件状态提供了基础,但是控件状态是一个自定义的状态保持机制。为了充分利用控件状态,您必须编写代码来保存和加载控件状态。

 


隐藏域
可以在页上的隐藏域中存储特定于页的信息,作为维护页的状态的一种方式。

如果使用隐藏域,最好在客户端上只存储少量经常更改的数据。

注意
如果使用隐藏域,则必须使用 HTTP POST 方法向服务器提交页,而不是使用通过页 URL 请求该页的方法(HTTP GET 方法)向服务器提交页。

使用隐藏域的优点
不需要任何服务器资源   隐藏域在页上存储和读取。
广泛的支持   几乎所有浏览器和客户端设备都支持具有隐藏域的窗体。
实现简单   隐藏域是标准的 HTML 控件,不需要复杂的编程逻辑。

使用隐藏域的缺点
潜在的安全风险   隐藏域可以被篡改。如果直接查看页输出源,可以看到隐藏域中的信息,这导致潜在的安全性问题。您可以手动加密和解密隐藏域的内容,但这需要额外的编码和开销。如果关注安全,请考虑使用基于服务器的状态机制,从而不将敏感信息发送到客户端。
简单的存储结构   隐藏域不支持复杂数据类型。隐藏域只提供一个字符串值域存放信息。若要存储多个值,必须实现分隔的字符串以及用来分析那些字符串的代码。您可以手动分别将复杂数据类型序列化为隐藏域以及将隐藏域反序列化为复杂数据类型。但是,这需要额外的代码来实现。如果您需要将复杂数据类型存储在客户端上,请考虑使用视图状态。视图状态内置了序列化,并且将数据存储在隐藏域中。
性能注意事项   由于隐藏域存储在页本身,因此如果存储较大的值,用户显示页和发送页时的速度可能会减慢。
存储限制   如果隐藏域中的数据量过大,某些代理和防火墙将阻止对包含这些数据的页的访问。因为最大数量会随所采用的防火墙和代理的不同而不同,较大的隐藏域可能会出现偶发性问题。如果您需要存储大量的数据项,请考虑执行下列操作之一:
将每个项放置在单独的隐藏域中。
使用视图状态并打开视图状态分块,这样会自动将数据分割到多个隐藏域。
不将数据存储在客户端上,将数据保留在服务器上。向客户端发送的数据越多,您的应用程序的表面响应时间越慢,因为浏览器需要下载或发送更多的数据。

 

Cookie
Cookie 用于在客户端上存储少量经常更改的信息。这些信息与请求一起发送到服务器。

使用 Cookie 的优点
可配置到期规则   Cookie 可以在浏览器会话结束时到期,或者可以在客户端计算机上无限期存在,这取决于客户端的到期规则。
不需要任何服务器资源   Cookie 存储在客户端并在发送后由服务器读取。
简单性   Cookie 是一种基于文本的轻量结构,包含简单的键值对。
数据持久性   虽然客户端计算机上 Cookie 的持续时间取决于客户端上的 Cookie 过期处理和用户干预,Cookie 通常是客户端上持续时间最长的数据保留形式。

使用 Cookie 的缺点
大小受到限制   大多数浏览器对 Cookie 的大小有 4096 字节的限制,尽管在当今新的浏览器和客户端设备版本中,支持 8192 字节的 Cookie 大小已愈发常见。
用户配置为禁用   有些用户禁用了浏览器或客户端设备接收 Cookie 的能力,因此限制了这一功能。
潜在的安全风险   Cookie 可能会被篡改。用户可能会操纵其计算机上的 Cookie,这意味着会对安全性造成潜在风险或者导致依赖于 Cookie 的应用程序失败。另外,虽然 Cookie 只能被将它们发送到客户端的域访问,历史上黑客已经发现从用户计算机上的其他域访问 Cookie 的方法。您可以手动加密和解密 Cookie,但这需要额外的编码,并且因为加密和解密需要耗费一定的时间而影响应用程序的性能。

注意
Cookie 通常用于为已知用户自定义内容的个性化情况。在大多数此类情况中,Cookie 是作为“标识”而不是“身份验证”。因此,通常保护用于标识的 Cookie 的方法是在 Cookie 中存储用户名、帐户名或唯一用户 ID(例如 GUID),然后用以访问站点的用户个性化结构中的信息。


 

查询字符串
查询字符串是在页 URL 的结尾附加的信息。

可以使用查询字符串来通过 URL 将数据提交回您的页或另一页上。查询字符串提供一种维护某些状态信息的简单但有限的方法。例如,它们是将信息从一页传送到另一页的简便的方法(例如,将产品号传递到将处理该产品号的另一页)。

使用查询字符串的优点
不需要任何服务器资源   查询字符串包含在对特定 URL 的 HTTP 请求中。
广泛的支持   几乎所有的浏览器和客户端设备均支持使用查询字符串传递值。
实现简单   ASP.NET 完全支持查询字符串方法,其中包含了使用 HttpRequest 对象的 Params 属性读取查询字符串的方法。

使用查询字符串的缺点
潜在的安全性风险   用户可以通过浏览器用户界面直接看到查询字符串中的信息。用户可将此 URL 设置为书签或发送给别的用户,从而通过此 URL 传递查询字符串中的信息。如果您担心查询字符串中的任何敏感数据,请考虑使用窗体(使用 POST 而不是查询字符串)中的隐藏域。
有限的容量   有些浏览器和客户端设备对 URL 的长度有 2083 个字符的限制。


服务器端状态管理
应用程序状态
ASP.NET 通过 HttpApplicationStatefrlrfSystemWebHttpApplicationStateClassTopic 类将应用程序状态提供为一种存储全局应用程序特定信息(对于整个应用程序都可见)的方法。应用程序状态变量实际上是 ASP.NET 应用程序的全局变量。

您可以在应用程序状态中存储应用程序特定的值,应用程序状态将由服务器来管理。

由多个会话共享并且不经常更改的数据是插入到应用程序状态变量的理想数据。

使用应用程序状态的优点
实现简单   应用程序状态易于使用,为 ASP 开发人员所熟悉,并且与其他 .NET Framework 类一致。
应用程序范围   由于应用程序状态可供应用程序中的所有页来访问,因此在应用程序状态中存储信息可能意味着仅保留信息的一个副本(例如,相对于在会话状态或在单独页中保存信息的多个副本)。

使用应用程序状态的缺点
应用程序范围   应用程序状态的范围可能也是一项缺点。在应用程序状态中存储的变量仅对于该应用程序正在其中运行的特定进程而言是全局的,并且每一应用程序进程可能具有不同的值。因此,不能依赖应用程序状态来存储唯一值或更新 Web 场和 Web 园服务器配置中的全局计数器。
数据持续性有限   因为在应用程序状态中存储的全局数据是易失的,所以如果包含这些数据的 Web 服务器进程被损坏(如因服务器崩溃、升级或关闭而损坏),将丢失这些数据。
资源要求   应用程序状态需要服务器内存,这可能会影响服务器的性能以及应用程序的可伸缩性。

应用程序状态的精心设计和实现可以提高 Web 应用程序性能。例如,如果将常用的、相关的静态数据集放置到应用程序状态中,则可以通过减少对数据库的数据请求总数来提高站点性能。但是,这里存在一种性能平衡。当服务器负载增加时,包含大块信息的应用程序状态变量就会降低 Web 服务器的性能。在移除或替换值之前,将不释放在应用程序状态中存储的变量所占用的内存。因此,最好只将应用程序状态变量用于更改不频繁的小型数据集。

 

会话状态
ASP.NET 提供了一种会话状态,该会话状态可作为 HttpSessionState 类或存储会话特定信息(仅在该会话中可见)的方法来使用。ASP.NET 会话状态将一个有限时间窗口内来自同一浏览器的请求标识为一个会话,并在该会话持续期间保留变量的值。

可以在会话状态中存储会话特定的值和对象,该会话状态对象将由服务器来进行管理并可用于浏览器或客户端设备。存储在会话状态变量中的理想数据是特定于单独会话的短期的、敏感的数据。

使用会话状态的优点
实现简单   会话状态功能易于使用,为 ASP 开发人员所熟悉,并且与其他 .NET Framework 类一致。
会话特定的事件   会话管理事件可以由应用程序引发和使用。
数据持久性   放置于会话状态变量中的数据可以经受得住 Internet 信息服务 (IIS) 重新启动和辅助进程重新启动,而不丢失会话数据,这是因为这些数据存储在另一个进程空间中。此外,会话状态数据可跨多进程保持(例如在 Web 场或 Web 园中)。
平台可伸缩性   会话状态可在多计算机和多进程配置中使用,因而优化了可伸缩性方案。
无需 Cookie 支持   尽管会话状态最常见的用途是与 Cookie 一起向 Web 应用程序提供用户标识功能,但会话状态可用于不支持 HTTP Cookie 的浏览器。但是,使用无 Cookie 的会话状态需要将会话标识符放置在查询字符串中(同样会遇到本主题在查询字符串一节中陈述的安全问题)。
可扩展性   您可通过编写自己的会话状态提供程序自定义和扩展会话状态。然后可以通过多种数据存储机制(例如数据库、XML 文件甚至 Web 服务)将会话状态数据以自定义数据格式存储。

使用会话状态的缺点
性能注意事项   会话状态变量在被移除或替换前保留在内存中,因而可能降低服务器性能。如果会话状态变量包含诸如大型数据集之类的信息块,则可能会因服务器负荷的增加影响 Web 服务器的性能。

 

配置文件属性
ASP.NET 提供了一个称为配置文件属性的功能,可让您存储特定于用户的数据。除了当用户的会话过期时配置文件数据不丢失这点与会话状态不同外,它与会话状态类似。配置文件属性功能使用 ASP.NET 配置文件,ASP.NET 配置文件以固定的格式存储并与单个用户相关联。ASP.NET 配置文件可让您轻松地管理用户信息,而无需创建和维护自己的数据库。此外,配置文件使用了一个强类型 API,您可以在应用程序中的任何位置访问该 API,从而使用用户信息。您可以在配置文件中存储任何类型的对象。ASP.NET 配置文件功能提供了一个通用存储系统,使您能够定义和维护几乎任何类型的数据,同时仍可用类型安全的方式使用数据。

使用配置文件属性的优点
数据持久性   放置在配置文件属性中的数据在 IIS 和辅助进程重新启动过程中得以保留而不会丢失数据,因为数据存储在一个外部机制中。此外,配置文件属性可跨多进程保持(例如在 Web 场或 Web 园中)。
平台可伸缩性   配置文件属性可在多计算机和多进程配置中使用,因而优化了可伸缩性方案。
可扩展性   为了使用配置文件属性,您必须对配置文件提供程序进行配置。ASP.NET 提供了一个 SqlProfileProvider 类,使您可以将配置文件数据存储在 SQL 数据库中,但您也可以创建自己的配置文件提供程序类将配置文件数据按自定义格式存储到自定义存储机制中,例如 XML 文件甚至 Web 服务。

使用配置文件属性的缺点
性能注意事项   配置文件属性通常比使用会话状态慢,因为前者将数据持久保存到数据存储设备而非内存中。
额外的配置要求   与会话状态不同,配置文件属性功能需要使用相当数量的配置。若要使用配置文件属性,您不仅要对配置文件提供程序进行配置,还要预先配置您想要存储的所有配置文件属性。
数据维护   配置文件属性需要一定的维护。因为配置文件数据持久保存到存储设备中,所以必须确保在数据陈旧时,应用程序调用由配置文件提供程序提供的相应清理机制。

 

数据库支持
在某些情况中,您可能希望使用数据库支持来维护网站上的状态。通常,数据库支持与 Cookie 或会话状态结合在一起使用。例如,对于电子商务网站,普遍使用关系数据库维护状态信息,其原因是:
安全性
个性化
一致性
数据挖掘

下面是支持 Cookie 的数据库网站的常见功能:
安全性   访问者将帐户名称和密码键入到站点登录页中。站点结构通过登录值查询数据库以确定该用户是否有权使用您的站点。如果数据库确认该用户信息有效,网站将把包含该用户的唯一 ID 的有效 Cookie 分发到客户端计算机上。站点授予该用户访问权限。
个性化   通过站点中存储的安全性信息,您的站点能够借助读取客户端计算机上的 Cookie 来区分站点上的每一用户。通常,站点在数据库中具有信息,描述用户的首选项(由唯一 ID 标识)。此关系通称作个性化。站点可以使用在 Cookie 中包含的唯一 ID 获知用户的首选项,然后向用户提供与用户的特定愿望相关并在一段时间内对用户首选项作出反应的内容和信息。
一致性   如果您已创建了一个商业网站,您可能想要在站点上保留所购买的物品和服务的交易记录。这些信息能够可靠地保存在您的数据库中并通过用户的唯一 ID 来引用。它可用于确定购买交易是否完成,还可确定如果购买交易失败所应采取的操作步骤。这些信息还可用于通知用户使用您的站点所下的订单的状态。
数据挖掘   有关站点使用、访问者或产品交易的信息能够可靠地存储在数据库中。例如,业务发展部门可能希望使用从该站点收集的这些数据确定下一年的产品线或分销策略。市场营销部门可能希望查看有关您的站点的用户的人口统计信息。设计和支持部门可能希望查看交易并记下购买过程可以改进的区域。诸如 Microsoft SQL Server 之类的大多数企业级关系数据库提供了可适用于大多数数据挖掘项目的可扩展工具集。

在上述方案中通过将网站设计为在每一一般性阶段使用唯一 ID 重复查询该数据库,该站点对状态进行维护。在此方法中,用户感受到站点正记住和响应其本人。

使用数据库维护状态的优点
安全性   访问数据库需要严格的身份验证和授权。
存储容量   可以根据需要在数据库中存储尽可能多的信息。
数据持久性   可以根据需要在尽可能长的时间内存储数据库信息,这些信息不受 Web 服务器可用性的影响。
可靠性和数据完整性   数据库包括多种用于维护有效数据的功能,其中包括触发器和引用完整性、事务等。通过在数据库中(而不是在会话状态等对象中)保存有关事务的信息,可以更为方便地从错误恢复。
可访问性   存储在数据库中的数据可供众多的信息处理工具访问。
广泛的支持   有大量数据库工具可供使用,并且有许多自定义配置可供使用。

使用数据库维护状态的缺点
复杂性   使用数据库支持状态管理需要更复杂的硬件和软件配置。
性能注意事项   不佳的关系数据模型结构可能导致可伸缩性问题。此外,对数据库执行过多的查询可能会影响服务器性能。