Session使用注意事项
网络整理 - 07-26
使用进程内会话状态模式时请考虑下面的限制: 使用进程内会话状态模式时,如果 aspnet_wp.exe 或应用程序域重新启动,则会话状态数据将丢失。这些重新启动通常会在下面的情况中发生:
在应用程序的 Web.config 文件的 <processModel> 元素中,设置一个导致新进程在条件被满足时启动的属性,例如 memoryLimit。
修改 Global.asax 或 Web.config 文件。
更改到 Web 应用程序的 \Bin 目录。
用杀毒软件扫描并修改 Global.asax 文件、Web.config 文件或 Web 应用程序的 \Bin 目录下的文件。
如果在应用程序的 Web.config 文件的 <processModel> 元素中启用了网络园模式,请不要使用进程内会话状态模式。否则将发生随机数据丢失。
我把Access数据库放到\bin下面去了,导致Session老是失效搞得我焦头烂额,去仔细阅读了一下MSDN里的《会话状态》,才发现了这些:
.NET Framework 开发员指南
会话状态 [C#]
请参见
ASP.NET 状态管理
语言
C#
Visual Basic
全部显示
ASP.NET 提供 Web 应用程序需要的跨请求状态信息(购物车、数据滚动等)基础结构,并带有内置的会话状态功能,使您可以采取以下操作:
对从单个浏览器客户端到服务器上逻辑应用程序会话的请求进行自动识别和分类。
将会话范围的数据存储在服务器上以供跨多个浏览器请求使用。
引发适当的可在应用程序代码中处理的会话生存期管理事件(Session_OnStart、Session_OnEnd 等)。
注意 Session_OnEnd 事件仅支持进程内会话状态模式。如果您使用状态服务器或 SQL Server 模式,则不会引发该事件。
如果浏览器不在指定的超时时间内重新访问应用程序,则自动释放会话数据。
本主题提供会话状态的概述,介绍如何标识和跟踪活动 ASP.NET 会话;解释会话状态存储和一般结构,并以一个高级代码示例作为总结。
会话状态概述
HTTP 是一个无状态的协议,这意味着它不自动指示一个请求序列是否都来自相同的客户端,甚至不指示单个浏览器实例是否仍活跃地查看某个页或站点。因此,如果没有其他基础结构的帮助,要想生成需要维护某些跨请求状态信息的 Web 应用程序,如购物车、数据滚动等,就可能会非常困难。
ASP.NET 提供以下会话支持:
便于使用的会话状态功能,该功能是 ASP 开发人员所熟悉的,与其他 .NET Framework API 兼容。
可靠的会话状态功能,可以经受得住 Internet 信息服务 (IIS) 重新启动和辅助进程重新启动而不丢失会话数据。
可缩放的会话状态功能,该功能可用于网络场(多计算机)和网络园(多进程)两种情况,使管理员可以将更多的处理器分配给 Web 应用程序以提高它的可缩放性。
用于不支持 HTTP Cookie 的浏览器的会话状态功能。
对于核心会话状态方案,其吞吐量相当于(或高于)ASP 的吞吐量(当向购物车放入项时 50/50 读/写,修改访问的最后一页,验证信用卡详细信息等)。
但是,会话状态不跨 Web 应用程序边界保持。如果执行期间一个 Web 应用程序切换到另一个应用程序,则会话信息不能用于新应用程序。
标识会话
每个活动的 ASP.NET 会话都是使用 120 位的 SessionID 字符串进行标识和跟踪的,该字符串只包含 URL 中所允许使用的 ASCII 字符。SessionID 值是使用保证唯一性和随机性的算法生成的,其中保证唯一性的目的是确保会话不冲突,保证随机性的目的是确保怀有恶意的用户不能使用新的 SessionID 来计算现有会话的 SessionID。
根据配置应用程序设置的方式,通过 HTTP Cookie 或嵌套有 SessionID 字符串的修改的 URL 跨客户端-服务器请求与 SessionID 字符串进行通信。
会话状态存储
ASP.NET 提供一个简单、易于使用的会话状态模型,您可以使用该模型跨多个 Web 请求存储任意数据和对象。它使用基于字典的、内存中的对象引用(这些对象引用存在于 IIS 进程中)缓存来完成该操作。使用进程内会话状态模式时请考虑下面的限制:
使用进程内会话状态模式时,如果 aspnet_wp.exe 或应用程序域重新启动,则会话状态数据将丢失。这些重新启动通常会在下面的情况中发生:
在应用程序的 Web.config 文件的 <processModel> 元素中,设置一个导致新进程在条件被满足时启动的属性,例如 memoryLimit。
修改 Global.asax 或 Web.config 文件。
更改到 Web 应用程序的 \Bin 目录。
用杀毒软件扫描并修改 Global.asax 文件、Web.config 文件或 Web 应用程序的 \Bin 目录下的文件。
如果在应用程序的 Web.config 文件的 <processModel> 元素中启用了网络园模式,请不要使用进程内会话状态模式。否则将发生随机数据丢失。
在进程外模式中,.NET 状态服务器不是保留活动对象,而是将会话状态存储在内存中。在这种模式中,辅助进程直接与状态服务器对话。在 SQL 模式中,会话状态存储在 SQL Server 数据库中,辅助进程直接与 SQL 对话。ASP.NET 辅助进程这时能够利用该简单的存储服务,方法是在每个 Web 请求结束时在客户端的 Session 集合中(使用 .NET 序列化服务)序列化并保存所有对象。当客户端重新访问服务器时,相关的 ASP.NET 辅助进程从状态服务器中以二进制流的形式检索这些对象,将它们反序列化为实时实例,并将它们放置回对请求处理程序公开的新 Session 集合对象。
在 SQL 模式中,也可以将会话状态配置为在故障转移群集中工作。故障转移群集是两个或更多相同的冗余 Web 服务器,它们将会话数据存储在一台单独的计算机上的 SQL Server 数据库中。有关如何设置此配置的信息,请参见配置 SQL Server 模式。
通过有效地将会话数据的存储与应用程序对它的使用分开,ASP.NET 支持许多功能强大的方案,而这些方案对于 ASP 的早期版本是不可用的:
因为用于会话状态的内存不在 ASP.NET 辅助进程中,所以可以实现从应用程序故障的恢复。
因为所有状态与个别辅助进程不存储在一起,所以如果由于访问冲突导致进程故障,或者在出现死锁或内存泄漏的情况下进程被 IIS 管理服务强行重新启动,状态也不会丢失。
跨多个辅助进程对应用程序进行分区。
因为所有状态与辅助进程不存储在一起,您可以干净地跨多个进程对应用程序进行分区。这种分区可以显著地提高多个进程的计算机上应用程序的可用性和可缩放性。此外,因为它将每个辅助进程与单个计算机关联起来,所以 ASP.NET 能够消除跨处理器锁争用,这是 ASP 早期版本中主要的可缩放性瓶颈之一。
跨多个网络场计算机对应用程序进行分区。
因为所有状态与辅助进程不存储在一起,所以您可以跨运行于多个计算机上的多个辅助进程对应用程序进行分区。我们有时需要在运行于不同计算机上的辅助进程和状态服务间传达状态,有时需要在运行于相同计算机上的进程和服务器间传达状态,而这两种传达状态的模型几乎是相同的。不管是哪种情况,每个网络场只能有一个状态服务器。
会话状态结构
基于 ASP.NET 的应用程序使用基于事件的执行组织启用多个 .NET Framework 类模块来参与单个 Web 请求的处理。
SessionState 模块
.NET Framework 通过 SessionStateModule 类(从 IHttpModule 派生)实现会话状态,该类参与基于 .NET 的应用程序所接收的每个请求的执行。SessionStateModule 负责生成或获得唯一的 SessionID 字符串,并负责存储状态数据和从外部状态提供程序检索状态数据。
会话状态集合
SessionState 类公开两个状态集合:Contents 和 StaticObjects。Contents 集合公开已直接通过代码添加到会话状态集合的所有变量项。例如:
[Visual Basic]
'' Visual Basic code from within a page, a handler, or Global.asax.
Session("Message") = "MyMsg"
Session("AppStartTime") = Now
[C#]
// C# code from within a page, a handler, or Global.asax.
Session["Message"] = "MyMsg";
Session["AppStartTime"] = DateTime.Now;
为了与 ASP 的早期版本兼容,还可以通过应用程序对象上的 Contents 属性访问这些值,如下面的示例所示。
[Visual Basic]
'' Visual Basic code from within a page, a handler, or Global.asax.
Session.Contents("Message") = "MyMsg"
Session.Contents("AppStartTime") = Now
[C#]
// C# code from within a page, a handler, or Global.asax.
Session.Contents["Message"] = "MyMsg";
Session.Contents["AppStartTime"] = DateTime.Now;
StaticObjects 集合公开所有已经通过 Global.asax 文件中带有“Session”范围的 <object runat="server"> 标记添加到会话状态集合的变量项。例如:
'' Global.asax definition.
<OBJECT RUNAT="SERVER" SCOPE="SESSION" ID="MyInfo" PROGID="scripting.Dictionary">
</OBJECT>
对象不能从 ASP.NET 应用程序内的其他任何地方添加到 StaticObjects 集合中。如果用户试图直接通过代码添加对象,则集合将引发一个 NotSupportedException。
注意 ASP.NET 页编译器在页编译时自动将成员引用插入到存储在 StaticObjects 集合中的所有对象中。
页开发人员可以在页请求时直接访问 Session 对象,而无需通过 StaticObjects 集合,如下面的示例所示:
<html>
</body>
Number of entries: <%= MyInfo.Count %>
<body>
</html>
会话状态的配置和启动
ASP.NET 中有三种会话状态模式。您可以在进程内、状态服务器和 SQL Server 之间选择。不管选择何种模式,基本的配置过程都是一样的。
ASP.NET 通过两个阶段配置会话状态。首先,将会话状态模块插入 HTTP 请求。默认情况下,这是在整个计算机 Machine.config 文件中配置层次结构的根完成的。
下面的示例显示了 Machine.config 文件中的一个示例项。为使配置文件正常工作,您必须为适当的 System.Web.SessionState.SessionStateModule 程序集版本提供完全限定程序集名称。此版本通常是与应用程序使用的 .NET Framework 版本关联的版本。有关如何获取完全限定程序集名称的信息,请参见程序集名称。
<httpmodules>
...
<!-- You must supply a valid fully qualified assembly name here. -->
<!-- For this example to work correctly, the version number for -->
<!-- the referenced assemby must match the version installed on -->
<!-- your computer by the .NET Framework. -->
<add name="sessionState" type="System.Web.SessionState.SessionStateModule, Version=1.0.3300.0,Culture=neutral,PublicKeyToken=b77a5c561934e089" />
...
</httpmodules>
然后,根据要使用的会话状态,在 <sessionState> 配置元素中设置适当的会话状态服务属性。
配置进程内模式
进程内模式是默认的会话状态模式。若要使用进程内模式,请将 <sessionState> 元素的 mode 属性设置为 Inproc。
下面显示了进程内模式的一个配置设置示例。
<configuration>
<system.web>
<sessionState mode="Inproc"
cookieless="false"
timeout="20"/>
</sessionState>
</system.web>
</configuration>
配置状态服务器模式
若要使用状态服务器,必须首先确保 ASP.NET 状态服务运行在用于会话存储的远程服务器上。此服务与 ASP.NET 和 Visual Studio .NET 一起安装在以下位置:
systemroot\Microsoft.NET\Framework\versionNumber\aspnet_state.exe
然后,在应用程序的 Web.config 文件中,将 <sessionState> 元素的 mode 属性设置为 StateServer。最后,将 connectionString 属性设置为 tcpip=serverName:portNumber。
下面是状态服务器模式的一个配置设置示例。
<configuration>
<system.web>
<sessionState mode="StateServer"
stateConnectionString="tcpip=dataserver:42424"
cookieless="false"
timeout="20"/>
</sessionState>
</system.web>
</configuration>
配置 SQL Server 模式
若要使用 SQL Server,首先在将存储会话状态的 SQL Server 计算机上,运行 InstallSqlState.sql 或 InstallPersistSqlState.sql。两个脚本均创建一个名为 ASPState 的数据库,它包含若干存储过程。两个脚本间的差异在于放置 ASPStateTempApplications 和 ASPStateTempSessions 表的位置。InstallSqlState.sql 脚本将这些表添加到 TempDB 数据库,该数据库在计算机重新启动时将丢失数据。相反,InstallPersistSqlState.sql 脚本将这些表添加到 ASPState 数据库,该数据库允许在计算机重新启动时保留会话数据。
默认情况下,两个脚本文件均安装在下面的位置:
systemroot\Microsoft.NET\Framework\versionNumber
然后,在应用程序的 Web.config 文件中,将 <sessionState> 元素的 mode 属性设置为 SQLServer。最后,将 sqlConnectionString 属性设置为 Integrated Security=SSPI;data source=serverName;。
下面显示了 SQL Server 模式的一个配置设置示例。
<configuration>
<system.web>
<sessionState mode="SQLServer"
sqlConnectionString=" Integrated Security=SSPI;data source=dataserver;"
cookieless="false"
timeout="20"/>
</sessionState>
</system.web>
</configuration>
在 SQL Server 模式中,也可以将会话状态配置为在故障转移群集中工作。故障转移群集是两个或更多相同的冗余 Web 服务器,它们将会话数据存储在一台单独的计算机上的 SQL Server 数据库中。如果一个 Web 服务器出现故障,群集中的另一个服务器会接管它的工作,为请求提供服务,会话数据不会丢失。若要配置故障转移群集,请将 Web 服务器的 Web.config 文件中的 <machinekey> 元素设置为相同的值。然后将 Web 服务器的 SQL 连接字符串设置为指向计算机上存储会话数据的 SQL Server 数据库。
高级代码示例
下面的示例显示如何以只读方式访问现有的会话状态数据,以动态生成包含用户信息和个人股票证券信息的页。
[Visual Basic]
<%@ Language=VB EnableSessionState=true %>
<html>
<head>
<script runat="server">
Sub Page_Load(ByVal Sender as Object, ByVal E as EventArgs)
'' Obtain data table of user''s personal stock data.
Dim MyStocks as DataTable
Dim Stock as DataRow
MyStocks = _
CType(Session("PersonalStockData"), DataTable)
'' Update HTML output with session values.
Name.InnerText = Session("FirstName").ToString()
SpouseVal.InnerText = Session("SpouseName").ToString()
For Each Stock In MyStocks.Rows
StockList.AddItem(Stock("Symbol") & ": " & Stock("Name"))
Next
End Sub
</script>
</head>
<body>
Hi <span id="Name" runat=server/>, your spouse is: <span id="SpouseVal" runat="server"/>.
Here are the stocks you and your spouse currently own:
<acme:listbox id="StockList" runat="server">
<! — List box is dynamically populated from code. -->
</acme:listbox>
</body>
</html>
[C#]
<%@ Language=C# EnableSessionState=true %>
<html>
<head>
<script runat=server>
void Page_Load(Object Sender, EventArgs E) {
// Obtain data table of user''s personal stock data.
DataTable MyStocks =
(DataTable)Session["PersonalStockData"];
// Update HTML output with session values.
Name.InnerText = Session["FirstName"].ToString();
SpouseVal.InnerText = Session["SpouseName"].ToString();
foreach (DataRow Stock in MyStocks.Rows) {
StockList.AddItem(Stock["Symbol"] + ": "
+ Stock["Name"]);
}
}
</script>
</head>
<body>
Hi <span id="Name" runat="server"/>, your spouse is: <span id="SpouseVal" runat="server"/>.
Here are the stocks you and your spouse currently own:
<acme:listbox id="StockList" runat="server">
<! — List box is dynamically populated from code. -->
</acme:listbox>
</body>
</html>