- 精品下载 | 实用查询 | 词典查询 | 桌面壁纸 | 网址 | 笑话 | FLASH频道 | 天气文章资讯 | 站长工具 | 证件办理 | 闪字生成 | 广告代码 | 在线手册 | 有问必答
您现在的位置: 蓝派网 >> 文章中心 >> 网络编程 >> .NET >> ASP.NET >> 正文
站内文章搜索:           

在ASP.NET中实现会话状态基础(二)

作者:佚名    文章来源:Microsoft    更新时间 :2007-5-30 15:39:26
状态序列化和反序列化

  使用进程内模式时,对象作为各自类的活动实例存储在会话状态中。如果未发生真正的序列化和反序列化,则表示您实际上可以在 Session 中存储您创建的任何对象(包括无法序列化的对象和 COM 对象),并且访问它们的开销也不会太高。如果您选择进程外状态提供程序,又是另外一种情况。

  在进程外体系结构中,会话值将从本地存储介质(外部 AppDomain 数据库)复制到处理请求的 AppDomain 的内存中。需要使用序列化/反序列化图层完成该任务,并表示进程外状态提供程序的某项主要成本。这种情况对代码产生的主要影响是只能在会话词典中存储可序列化的对象。

  根据所涉及的数据类型,ASP.NET 使用两种方法对数据进行序列化和反序列化。对于基本类型,ASP.NET 使用经过优化的内部序列化程序;对于其他类型(包括对象和用户定义的类),ASP.NET 使用 .NET 二进制格式化程序。基本类型包括字符串、日期时间、布尔值、字节、字符以及所有的数字类型。对于这些类型,使用量身制作的序列化程序要比使用默认的常用 .NET 二进制格式化程序更快。

  经过优化的序列化程序没有公开发布,也没有以文档形式提供。它仅仅是二进制读取器/写入器,并且使用简单但有效的存储架构。该序列化程序使用 BinaryWriter 类写入一个字节表示类型,然后写入一个字节表示该类型对应的值。读取序列化的字节时,该类首先提取一个字节,检测要读取的数据类型,然后对 BinaryReader 类调用特定类型的 ReadXxx 方法。

  请注意,布尔值和数字类型的大小是众所周知的,但对字符串并非如此。在基础数据流上,字符串始终带有一个固定长度的前缀(一次编写 7 位整数代码),读取器根据这一事实来确定字符串的正确大小。而日期值是通过只写入构成日期的标记总数来保存的。因此,要对会话执行序列化操作,日期应为 Int64 类型。

  只要将包含的类标记为可序列化的类,便可以使用 BinaryFormatter 类对更复杂的对象(以及自定义对象)执行序列化操作。所有非基本类型都采用相同的类型 ID 进行标识并与基本类型存储在同一个数据流中。总之,序列化操作会导致性能下降 15% 至 25%。但请注意,这是基于假定使用基本类型所进行的粗略估计。使用的类型越复杂,开销越大。

  如果不大量使用基本类型,很难实现有效的会话数据存储。因此,至少在理论上,使用三个会话槽保存对象的三个不同的字符串属性要比对整个对象进行序列化好。但是,如果要序列化的对象包含 100 个属性,那该怎么办呢?是要使用 100 个槽,还是只使用一个槽?在许多情况下,更好的方法是将复杂的类型转换为多个简单的类型。这种方法基于类型转换器。“类型转换器”是一种轻便的序列化程序,它以字符串集合的形式返回类型的关键属性。类型转换器是使用特性与基类绑定在一起的外部类。由类型编写者决定保存哪些属性以及如何保存。类型转换器对于 ViewState 存储也有帮助,它代表的是比二进制格式化程序更有效的会话存储方法。

  会话的生命周期

  关于 ASP.NET 会话管理,重要的一点是,仅当将第一个项目添加到内存词典中时,会话状态对象的生命周期才开始。仅在执行如下代码片断后,才可以认为 ASP.NET 会话开始。

Session["MySlot"] = "Some data";

  Session 词典通常包含 Object 类型,要向后读取数据,需要将返回的值转换为更具体的类型。

string data = (string) Session["MySlot"];

  当页面将数据保存到 Session 中时,会将值加载到 HttpSessionState 类包含的特制的词典类中。完成当前处理的请求时,会将词典的内容加载到状态提供程序中。如果由于未通过编程方式将数据放入词典而导致会话状态为空,则不会将数据序列化到存储介质中,而且更重要的是,不会在 ASP.NET Cache、SQL Server 或 NT 状态服务中创建槽来跟踪当前会话。这是出于性能方面的原因,但会对处理会话 ID 的方式产生重要影响:将为每个请求生成一个新的会话 ID,直到将某些数据存储到会话词典中。

  需要将会话状态与正在处理的请求连接时,HTTP 模块会检索会话 ID(如果它不是启动请求),并在配置的状态提供程序中寻找它。如果没有返回数据,HTTP 模块将为请求生成一个新的会话 ID。这可以很容易地通过以下页面进行测试:

<%@ Page Language="C#" Trace="true" %>;
</html>;
<body>;
<form runat="server">;
<asp:button runat="server" text="Click" />;
</form>;
</body>;
</html>;

  无论何时单击该按钮并返回页面,都将生成新的会话 ID,同时记录跟踪信息。


图 3:在没有将数据存储到会话词典中的应用程序中,为每个请求生成一个新的会话 ID。


  Session_OnStart 事件的情况如何呢?也会为每个请求引发该事件吗?如果应用程序定义 Session_OnStart 处理程序,则会始终保存会话状态,即使会话状态为空。因此,对于第一个请求之后的所有请求来说,会话 ID 始终为常量。仅在确实必要时,才使用 Session_OnStart 处理程序。

  如果会话超时或被放弃,下次访问无状态应用程序时,其会话 ID 不会发生改变。经过设计后,即使会话状态过期,会话 ID 也能持续到浏览器会话结束。也就是说,只要浏览器实例相同,就始终使用同一个会话 ID 表示多个会话。

  Session_OnEnd 事件标志着会话的结束,并用于执行终止该会话所需的所有清除代码。但请注意,只有 InProc 模式支持该事件,也就是说,只有将会话数据存储在 ASP.NET 辅助进程中时才支持该事件。对于要引发的 Session_OnEnd 事件来说,必须首先存在会话状态,这意味着必须在该会话状态中存储一些数据,并且必须至少完成一个请求。

  在 InProc 模式下,作为项目添加到缓存中的会话状态被赋予一个可变过期时间策略。可变过期时间表示如果某个项目在一定时间内没有使用,将被删除。在此期间处理的任何请求的过期时间都将被重置。会话状态项目的时间间隔被设置为会话超时。用来重置会话状态过期时间的技术非常简单和直观:会话 HTTP 模块只读取 ASP.NET Cache 中存储的会话状态项目。如果知道 ASP.NET Cache 对象的内部结构,该模块将进行计算以重新设置可变过期时间。因此,当缓存项目过期时,会话已超时。

  过期的项目将自动从缓存中删除。状态会话模块作为此项目的过期时间策略的一部分,也代表了一个删除回调函数。缓存将自动调用删除函数,删除函数然后将引发 Session_OnEnd 事件。如果应用程序通过进程外组件来执行会话管理,则永远不会引发结束事件。
 Cookieless 会话

  每个活动 ASP.NET 会话都是使用仅由 URL 允许的字符组成的 120 位字符串标识的。会话 ID 是使用随机数生成器 (RNG) 加密提供程序生成的。该服务提供程序返回一个包含 15 个随机生成数的序列(15 字节 x 8 位 = 120 位)。随机数数组然后被映射到有效的 URL 字符并以字符串形式返回。

  会话 ID 字符串被发送到浏览器,然后通过以下两种方式之一返回服务器应用程序:使用 Cookie(就像在传统 ASP 中一样)或经过修改的 URL。默认情况下,会话状态模块将在客户端创建 HTTP Cookie,但是可以使用嵌入会话 ID 字符串的修改后的 URL(特别是对于不支持 Cookie 的浏览器)。采用哪种方法取决于应用程序的 web.config 文件中所存储的配置设置。要配置会话设置,可以使用 <sessionState>; 区段和 Cookieless 特性。

<sessionState cookieless="true|false" />;

  默认情况下,Cookieless 特性为 false,表示使用了 Cookie。实际上,Cookie 只是 Web 页放在客户端硬盘上的一个文本文件。在 ASP.NET 中,Cookie 由 HttpCookie 类的一个实例来表示。通常,Cookie 包含名称、值集合和过期时间。Cookieless 特性被设置为 false 时,会话状态模块实际上将创建一个名为 ASP.NET_SessionId 的 Cookie 并将会话 ID 存储在其中。下面的伪代码显示了创建 Cookie 的过程:

HttpCookie sessionCookie;
sessionCookie = new HttpCookie("ASP.NET_SessionId", sessionID);
sessionCookie.Path = "/";

  会话 Cookie 的过期时间很短,在每个请求成功后更新过期时间。Cookie 的 Expires 属性表示 Cookie 在客户端的过期时间。如果未显式设置会话 Cookie,Expires 属性将默认为 DateTime.MinValue,即 .NET Framework 允许的最小时间单位。

  要禁用会话 Cookie,请在配置文件中将 Cookieless 特性设置为 true,如下所示:

<configuration>;
<system.web>;
<sessionState Cookieless="true" />;
</system.web>;
</configuration>;

  此时,假设您请求以下 URL 处的页面:

http://www.contoso.com/sample.aspx

  浏览器地址栏中实际显示的内容会有所不同,现在包含会话 ID,如下所示:

http://www.contoso.com/(5ylg0455mrvws1uz5mmaau45)/sample.aspx

  实例化会话状态 HTTP 模块时,该模块将检查 Cookieless 特性的值。如果为 true,则将请求重定向 (HTTP 302) 到经过修改的包含会话 ID(紧跟在页面名称前)的虚拟 URL。再次处理请求时,请求中会包含该会话 ID。如果请求启动新的会话,HTTP 模块将生成新的会话 ID,然后重定向该请求。如果回传请求,则会话 ID 已经存在,因为回传使用相对 URL。

  使用 Cookieless 会话的缺点是,如果调用绝对 URL,将丢失会话状态。使用 Cookie 时,您可以清除地址栏,转至其他应用程序,然后返回上一个应用程序并检索相同的会话值。如果在禁用会话 Cookie 时执行此操作,将丢失会话数据。例如,以下代码将打断该会话:

<a runat="server" href="/code/page.aspx">;Click</a>;

  如果需要使用绝对 URL,请通过一些小技巧手动将会话 ID 添加到 URL 中。您可以对 HttpResponse 类调用 ApplyAppPathModifier 方法。

<a runat="server"
href=<% =Response.ApplyAppPathModifier("/code/page.aspx")%>; >;Click</a>;

  ApplyAppPathModifier 方法将使用表示 URL 的字符串,并返回嵌入会话信息的绝对 URL。例如,需要从 HTTP 页面重定向到 HTTPS 页面时,此技巧特别有用。

  小结

  会话状态最初由传统的 ASP 引入,它是基于词典的 API,使开发人员能够存储会话期间的自定义数据。在 ASP.NET 中,会话状态支持以下两种主要功能:Cookieless 会话 ID 存储和传输,以及会话数据实际存储的状态提供程序。为实现这两种新功能,ASP.NET 利用 HTTP 模块控制会话状态与正在处理的请求上下文之间的绑定。

  在传统的 ASP 中,使用会话状态就是指使用 Cookie。在 ASP.NET 中已不再如此,因为可以使用 Cookieless 架构。借助 HTTP 模块的力量,可以分解请求的 URL 以使其包含会话 ID,然后将其重定向。接下来,HTTP 模块会从该 URL 中提取会话 ID 并使用它检索任何存储的状态。

  会话的物理状态可以存储在三个位置:进程内内存、进程外内存和 SQL Server 表。数据必须经过序列化/反序列化处理,才能供应用程序使用。HTTP 模块会在请求开始时将会话值从提供程序复制到应用程序的内存中。请求完成后,修改后的状态将返回提供程序。这种数据通信会对性能产生不同程度的不利影响,但是会大大增强可靠性和稳定性,也使对 Web Farm 和 Web Garden 体系结构的支持更容易实现。


发表评论】【打印此文】【关闭窗口】【点击数:
★好玩的休闲小游戏★
网友评论:
数据载入中,请稍后……