• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    公众号

实现ASP.NET会话状态存储提供者

原作者: [db:作者] 来自: [db:来源] 收藏 邀请

本文描述了一个自定义的会话状态存储提供者实现并示范一个范例提供者的实例。

ASP.NET 会话状态是为了简化 ASP.NET 应用程序在不同的源中存储用户会话数据而被设计的。默认时,会话状态的值和信息被存储在 ASP.NET 进程的内存中。作为选择,你可以把会话数据存储到一个状态服务器中,从而在一个单独的进程中保持会话数据并在 ASP.NET 应用程序被关闭或被重启的时候对其进行保留。另一个选择就是把会话数据存储到一个能够被多个 Web 服务器所共享的 SQL Server 数据库中。

你可以使用 ASP.NET 所提供的会话状态存储,或者你也可以实现自定义会话状态存储提供者。如下就是创建自定义的会话状态存储提供者时的两个主要原因。

  • 你需要在不同于 SQL Server 的数据源中存储会话状态信息(如 FoxPro 数据库或者 Oracle 数据库)。
  • 你需要使用不同于被承载在 .NET Framework 之上的提供者所使用的数据库结构来管理会话状态信息(例如,可能是某个企业或网站中以一个预定义的架构被存储在现有的 SQL Server 数据库中的购物车数据)。

你可以通过创建一个 SessionStateStoreProviderBase 的继承类来实现一个自定义的会话状态存储提供者。关于更多信息,请参考本文的稍后部分:[必需的类]。

会话状态模块

在不同时间段内产生了请求的时候,SessionStateModule 类就会调用会话状态存储提供者来读取并写入会话数据。在请求刚开始的时候,SessionStateModule 通过调用 GetItemExclusive 方法从数据源中获取数据,或者在 EnableSessionState 页面参数被设置成 ReadOnly 的时候调用 GetItem 方法。在请求结束的时候,如果会话状态的值已经被更改,那么 SessionStateModule 就会调用 System.Web.SessionState.SessionStateStoreProviderBase.SetAndReleaseItemExclusive(System.Web.HttpContext,System.String,System.Web.SessionState.SessionStateStoreData,System.Object,System.Boolean) 方法把已经被更新的值写入到会话状态存储中。SessionStateModule 会调用 SessionStateStoreProviderBase 实现中的额外成员来初始化一个新的会话并在 System.Web.SessionState.HttpSessionState.Abandon 方法被调用的时候从数据存储中删除会话数据。SessionStateStoreProviderBase 类的每个成员在本文稍后的[必需的类]部分中被详细讨论。

SessionStateModule 能够检测它自已的 SessionID 属性值,胜于对对会话状态存储提供者的依赖。你可以通过创建一个继承自 ISessionIDManager 接口的类来实现自定义的 SessionIDModule。关于更多信息,请参考[ISessionIDManager 接口]中中所提供的备注。

SessionStateManager 将回复到 ASP.NET 进程的身份来访问任何已保护的资源(如某个数据库服务器)。你可以通过把 <sessionState> 配置元素的 useHostingIdentity 参数值设置成 false 的方式来指定 SessionStateModule 扮演由 IIS 所提供的身份。例如,如果你已经把 IIS 应用程序配置成使用被集成的 Windows 安全性并且你需要 ASP.NET 扮演由 IIS 所提供的并且与数据源连接所使用的身份相同的角色,然后在应用程序的 Web.config 文件的 <system.web> 配置段中指定 <identity impersonate="true" /> 并把 <sessionState> 配置元素的 useHostingIdentity 参数设置成 false。如果 useHostingIdentity 参数值为 true,那么在连接到数据源的时候,ASP.NET 将扮演处理器的身份,或者是存在于 <identity> 配置元素中的用户凭证。关于 ASP.NET 进程身份的更多信息,请参考:[配置 ASP.NET 的进程身份和 ASP.NET 的角色扮演]。

锁定会话存储数据

ASP.NET 应用程序通过多线程来支持多个并发请求。使多个并发的请求尝试对相同会话信息的访问变得可能。考虑一个框架集的多个框架全部访问相同应用程序的情节,框架集中每个框架的单独请求可以在 Web 服务器的不同线程中并发地被执行。如果每个框架源中的 ASP.NET 页面都对会话状态变量进行访问,那么你就可以使用多个线程并发地访问会话存储。要避免会话存储以及意外的会话状态行为中的数据出现冲突,SessionStateModule 和 SessionStateStoreProviderBase 类包括了在 ASP.NET 页面的特定会话期间专门对会话存储项进行锁定的功能。注意在 EnableSessionState 参数被标记为 ReadOnly 的时候不会锁定会话存储项。但是,相同应用程序中的其他 ASP.NET 页面可能会改写会话存储,所以针对于只读会话数据的请求仍然需要等待被锁定的数据被释放才能被执行。

在请求刚开始调用 System.Web.SessionState.SessionStateStoreProviderBase.GetItemExclusive(System.Web.HttpContext,System.String,System.Boolean,System.TimeSpan,System.Object,System.Web.SessionState.SessionStateActionFlags) 方法的时候会对会话存储数据进行锁定。在请求被完成的时候,锁定就会在 System.Web.SessionState.SessionStateStoreProviderBase.SetAndReleaseItemExclusive(System.Web.HttpContext,System.String,System.Web.SessionState.SessionStateStoreData,System.Object,System.Boolean) 方法的调用期间被释放。

如果 SessionStateModule 在 GetItemExclusive 或者 GetItem 方法的调用期间遇到了被锁定的会话数据,那么它将每隔半秒时间就对会话数据进行一次重新请求,直到锁定被释放或者会话数据被锁定的时间期限超出了 ExecutionTimeout 属性值中的上限为止。如果执行时间已经超过了期限,那么 SessionStateModule 将会调用 System.Web.SessionState.SessionStateStoreProviderBase.ReleaseItemExclusive(System.Web.HttpContext,System.String,System.Object) 方法来释放会话存储数据并同时对该会话存储数据进行重新请求。

在一个单独的线程中为当前请求调用 SetAndReleaseItemExclusive 方法之前,被锁定的会话存储数据可能已经通过 ReleaseItemExclusive 方法的调用而被释放。这可能引起 SessionStateModule 被设置并且释放已经被释放并且被另一个会话所更改的会话状态存储数据。要避免这种情况的出现,SessionStateModule 为每个请求都包括了一个锁定标识符用来更改被锁定的会话存储数据。会话存储数据只能够在与数据存储中的锁定标识符与通过 SessionStateModule 所提供的锁定标识符相匹配的时候才能够被更改。

删除已过期的会话存储数据

在为一个特定的会话而调用 Abandon 方法的时候,该会话的数据会使用 RemoveItem 方法从数据存储中被删除;否则数据将被保持在会话数据存储中并服务于该会话的未来请求。

删除已过期的会话数据的机制依赖于数据源的能力。如果数据源能够被配置成在每个会话超时的时候能删除已过期的会话数据,那么你就可以使用 SetItemExpireCallback 方法来引用 Session_OnEnd 的事件代表并在删除已过期的会话数据的时候对其进行触发。

ApplicationName

要维护会话的应用范围,会话状态存储提供者应该为每个应用程序都分别存储唯一的会话信息。这允许多个 ASP.NET 应用程序在复制会话标识符时不出现冲突的情况之下使用相同的数据源。

因为会话状态存储提供者为每个应用程序分别存储了唯一的会话信息,所以你需要确保数据结构和查询中已经包括了对应用程序名称的更新。例如,如下所示的命令被用来从数据库中获取会话数据。

SELECT * FROM Sessions 
  WHERE SessionID = 'ABC123' AND ApplicationName = 'MyApplication'

作为选择,你还可以把会话标识符和与会话状态数据存储项相同的唯一标识符相同的应用程序名称进行联合存储。

必需的类

要实现一个会话状态存储提供者,就需要创建一个由抽象类 SessionStateStoreProviderBase 所继承的类。而 SessionStateStoreProviderBase 则继承自 ProviderBase 抽象类,所以你还必须实现 ProviderBase 类的必需成员。下表列出了你必须为 ProviderBase 和 SessionStateStoreProviderBase 抽象类而实现的属性和方法并分别为它们提供了描述信息。要查看每个成员的实现,请参考:[会话状态存储提供者的范例]。

必需的 ProviderBase 类成员
成员 描述
Initialize 方法 获取(如输入)提供者的名称和一个配置设定的 NameValueCollection 集合。这个方法被用来为提供者的实例设置属性值,包括在配置文件(Machine.config 或 Web.config)中被指定的特定的实现值和选项。
必需的 SessionStateStoreProvider 类成员
成员 描述

InitializeRequest 方法

获取当前请求的 HttpContext 并通过会话状态存储提供者来完成任何必需的初始化任务。

EndRequest 方法

获取当前请求的 HttpContext 并通过会话状态存储提供者来完成任何必需的清理任务。

Dispose 方法

通过会话状态存储提供者来释放任何不再使用的资源。

GetItemExclusive 方法

获取当前请求的 HttpContext 和当前请求的 SessionID。从会话数据存储中获取会话值和信息并在请求期间锁定数据存储中的会话项数据。GetItemExclusive 方法设置了几个输出参数的值来通知对 SessionStateModule 方法(与数据存储中当前会话状态项的状态相关)的调用。

如果没有在数据存储中找到任何会话项数据,GetItemExclusive 方法就会把被锁定的输出参数设置成 false 并返回 null。这将导致 SessionStateModule 为该请求调用 CreateNewStoreData 方法来创建一个新的 SessionStateStoreData 对象。

如果在数据存储中找到了会话项数据但是数据已经被锁定,GetItemExclusive 方法会把被锁定的输出参数设置成 true,在会话项被锁定的时候把 lockAge 输出参数设置成当前日期和时间与被锁定的时期和时间的和,把 lockId 参数设置成需要锁定的从数据存储中被获取的标识符,并返回 null。这会导致 SessionStateModule 在半秒的时间间隔之后再次调用 GetItemExclusive 方法来尝试获取该会话项信息并获得对数据的锁定。如果 lockAge 输出参数中所设置的值超出了 ExecutionTimeout 属性中的上限,那么 SessionStateModule 将会调用 ReleaseItemExclusive 方法来清除对会话项数据的锁定然后再次调用 GetItemExclusive 方法。

actionFlags 参数会在 regenerateExpiredSessionId 参数被设置成 true 的时候与无 Cookie 会话一起被使用。在 actionFlags 参数的值被设置成 InitializeItem(1)的时候表示会话数据存储中的该项是一个需要进行初始化的新会话。那么会话数据存储中未被初始化的项将通过调用 CreateUninitializedItem 方法而被创建。如果会话数据存储中的项是未被初始化的,那么 actionFlags 参数将被设置成 0。

如果你提供了对无 Cookie 会话的支持,那么你应该为当前会话项而把 actionFlags 输出参数设置成从会话数据存储中被返回的值。如果被请求会话存储项的 actionFlags 参数值等于 InitializeItem 枚举值(1),那么 GetItemExclusive 方法就应该在 actionFlags 输出参数被设置之后把数据存储中的值设置成 0。

GetItem 方法

这个方法完成与 GetItemExclusive 方法相同的工作,区别就是它不会尝试获取对数据存储中的会话项进行锁定。GetItem 方法就会在 EnableSessionState 参数被设置成 ReadOnly 的时候被调用。

SetAndReleaseItemExclusive 方法

获取当前请求的 HttpContext、当前请求的 SessionID、包含被存储的会话值的 SessionStateStoreData 对象、当前请求的锁定标识符,以及表示被存储的数据是否属于新的会话还是现有会话的值。

如果 newItem 参数被设置成 true,那么 SetAndReleaseItemExclusive 方法就会使用被提供的值在数据存储中插入一个新项。否则,则会使用被提供的值来更新数据存储中的现有项,并且对于数据的锁定也将被释放。只有与被提供的 SessionID 和锁定标识符的值相匹配的当前应用程序会话数据才会被更新。

在 SetAndReleaseItemExclusive 方法被调用之后,会通过 SessionStateModule 来调用 ResetItemTimeout 方法以更新会话项数据的过期日期和时间。

ReleaseItemExclusive 方法

获取当前请求的 HttpContext、当前请求的 SessionID,以及当前请求的锁定标识符并释放对于会话数据存储项的锁定。这个方法在 GetItem 或 GetItemExclusive 方法被调用并且数据存储所指定的被请求项已经被锁定,但是锁定时间已经超出了 ExecutionTimeout 的时候才会被调用。锁定通过这个方法被清除,并释放会话项以用于其他请求。

RemoveItem 方法

获取当前请求的 HttpContext、当前请求的 SessionID,以及当前请求的锁定标识符并在数据存储项与被提供的 SessionID、当前应用程序,以及被提供的锁定标识符相匹配的时候从数据存储中删除会话信息。这个方法在 Abandon 方法被调用的时候被调用。

CreateUninitializedItem 方法

获取当前请求的 HttpContext、当前请求的 SessionID,以及当前请求的锁定标识符并通过把 actionFlags 参数值设置成 InitializeItem 的方式在会话数据存储中添加一个未被初始化的项。

在 regenerateExpiredSessionId 参数被设置成 true 的时候,CreateUninitializedItem 方法会与无 Cookie 会话一起被使用,从而导致 SessionStateModule 在遇到一个已过期的 SessionID 的时候会重新生成一个新的 SessionID 值。

生成新 SessionID 值的进程需要浏览器被重定向到一个包含有生成了更新的 SessionID 的 URL。CreateUninitializedItem 方法在包含已过期 SessionID 的请求的初始化期间被调用。在 SessionStateModule 获得一个新的 SessionID 值并替换已过期的 SessionID 之后,它会调用 CreateUninitializedItem 方法为会话状态数据存储添加一个未被初始化的会话项。然后浏览器被重定向到包含生成了更新的 SessionID 值的 URL。会话数据存储中现有的未初始化会话项会确保在包括生成了更新的 SessionID 值并且没有出现错误的被重定向的请求的情况下会被替代为或被视为一个新的会话。

会话数据存储中未被初始化的项与生成了更新的 SessionID 相关联,并且只包含了默认值,包括期限日期和时间,以及一个与 GetItem 和 GetItemExclusive 方法的 actionFlags 参数相对应的值。会话状态存储中未被初始化的项应该在 actionFlags 参数中包括一个 InitializeItem 枚举值(1)。这个值通过 GetItem 和 GetItemExclusive 方法被传递到 SessionStateModule 并通知 SessionStateModule 当前会话是一个新的会话。SessionStateModule 然后将初始化这个新的会话并引发 Session_OnStart 事件。

CreateNewStoreData 方法

获取当前请求的 HttpContext 和当前会话的 Timeout 属性并返回一个拥有空的 ISessionStateItemCollection 对象、HttpStaticObjectsCollection 集合,以及被指定的 Timeout 值的全新 SessionStateStoreData 对象。ASP.NET 应用程序的HttpStaticObjectsCollection 集合能够通过调用 GetSessionStaticObjects 方法来获取。

SetItemExpireCallback 方法

获取被定义在 Global.asax 文件中并对 Session_OnEnd 事件进行引用的事件代表。如果会话状态存储提供者支持 Session_OnEnd 事件,那么将设置一个对于 SessionStateItemExpireCallback 参数的局部引用并通过该方法返回 true;否则,该方法返回 false。

范例提供者

要查看一个使用 System.Data.Odbc 命名空间中的类并在 Access 数据库中管理会话信息自定义会话状态存储提供者的实现范例,请参考:[会话状态存储提供者的范例]。


鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
上一篇:
ASP.NET生成HTML页(多种)发布时间:2022-07-10
下一篇:
ASP.NET设计模式(转)发布时间:2022-07-10
热门推荐
热门话题
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap