ASP.NET 自定义会话存储提供程序,兼容 Oracle、SQL
一个自定义会话存储提供程序库,允许使用 Oracle 来管理 ASP.NET 会话……不仅仅是 MS SQL Server,并且可以轻松扩展到其他数据库。
引言
本文旨在帮助将 Oracle 数据库用作 ASP.NET 会话的会话存储。本文也可以被视为构建您自己的自定义会话存储提供程序代码而不是使用默认的 SessionStore
的入门。
背景
Microsoft ASP.NET 框架附带了必要的代码块(实际上内置在 .NET 框架本身中)和 SQL 脚本(如果您是第一次使用 SQL Server 进行会话存储,则需要从命令行工具 aspnet_regsql.exe 在 Microsoft SQL Server 上安装)以在所有可配置模式下启动 ASP.NET 应用程序中的会话使用。
ASP.NET 默认的基于数据库的会话存储管理方法的缺点
- 默认会话存储提供程序(来自 .NET 和 ASP.NET 框架)只支持 Microsoft SQL Server——这显然是因为 Microsoft 不能直接编写代码来支持竞争对手的数据库。
- 默认会话存储——在 SQL Server 上占用大量空间,有许多表进入……如果您尝试使用从未用作 ASP.NET 会话存储的新 Microsoft SQL Server 数据库,那么您可能需要运行 aspnet_regsql.exe 命令行 EXE 来准备服务器上的数据库。
现在这第二点有更多子问题
- 所有 Web 应用程序都在 SQL Server 上使用公共数据库进行会话。缺乏默认隔离可能会使所有应用程序看起来都对会话管理或用户权限管理具有相同的要求,尽管超时等一些参数可以在每个应用程序的 web.config 级别进行配置。
- 备份会话数据,或自定义会话存储以实现记录用户 IP、用户活动等功能,这很复杂,因为您需要修改 Microsoft 的会话表设计,这可能会影响许多应用程序(由于缺乏隔离),而使用自己的表中的会话并改进会话表设计则不同。
好消息
尽管上述所有问题都限制了 ASP.NET 中会话管理的功能,但 Microsoft 一如既往地在这里为您提供帮助,如果没有这种帮助,我们将不得不完全自己处理会话,而不是像以下语法那样轻松地使用会话
// Writing to session
Session["userLoggedIn"] = "some username";
Session["userLastLogInDT"] = "2010-08-16 10:00:00";
//Reading from session.
Your last login was at: <%= Response.Write (Session["userLastLogInDT"]); %>
我们如何启动自定义提供程序
只需从 System.Web.SessionState
命名空间中的 SessionStateStoreProviderBase
类派生即可。这个 abstract
类在派生时将要求派生类实现其所有定义……如下所示
public override void Initialize(string name, NameValueCollection config)
public override void Dispose()
public override bool SetItemExpireCallback(SessionStateItemExpireCallback expireCallback)
public override void SetAndReleaseItemExclusive(HttpContext context,
string id,
SessionStateStoreData item,
object lockId,
bool newItem)
public override SessionStateStoreData GetItem(HttpContext context,
string id,
out bool locked,
out TimeSpan lockAge,
out object lockId,
out SessionStateActions actionFlags)
public override SessionStateStoreData GetItemExclusive(HttpContext context,
string id,
out bool locked,
out TimeSpan lockAge,
out object lockId,
out SessionStateActions actionFlags)
private SessionStateStoreData GetSessionStoreItem(bool lockRecord,
HttpContext context,
string id,
out bool locked,
out TimeSpan lockAge,
out object lockId,
out SessionStateActions actionFlags)
private string Serialize(SessionStateItemCollection items)
private SessionStateStoreData Deserialize(HttpContext context,
string serializedItems, int timeout)
public override void ReleaseItemExclusive(HttpContext context,
string id,
object lockId)
public override void RemoveItem(HttpContext context,
string id,
object lockId,
SessionStateStoreData item)
public override void CreateUninitializedItem(HttpContext context,
string id,
int timeout)
public override SessionStateStoreData CreateNewStoreData(
HttpContext context,
int timeout)
public override void ResetItemTimeout(HttpContext context,
string id)
public override void InitializeRequest(HttpContext context)
public override void EndRequest(HttpContext context)
MSDN 和其他地方有大量阅读材料,介绍了每个强制方法应该如何实现。
代码鸣谢
此处附加代码 (CustomSessionStoreProvider.cs) 中的大部分实现均来自另一篇类似的 Rob Smith MN 的 CodeProject 文章。
本文代码的不同之处在于,我添加了一个 DataAccess
类 (SessDataAccess.cs) 来隔离数据访问方法,这样只需通过配置更改即可将代码同时用于 Oracle 和 MS SQL Server。
此外,由于 DataAccess
方法的隔离,如果需要,可以轻松地将此代码扩展到 DB2、MySQL 和许多其他数据库引擎。
是的,本文的代码仍可被视为 RobSmith 代码的扩展。感谢 RobSmith。
关于附加代码
本文附带了一个相当专业的用法示例,包含源代码和测试页面。我相信所附代码可以为您提供一个“基础已完成”的实现,同时也能为您提供一个很好的起点,以便用您自己的功能进行扩展,或者扩展到更多的数据库引擎。
所附代码有两个重要文件,我将在这里稍作解释,以便您在需要添加内容时可以修改它们。
CustomSessionStoreProvider.cs
派生自会话存储提供程序基类。
对数据库上的 select
、update
、delete
和 insert
操作具有硬编码的 SQL 查询语句。所附代码不使用存储过程,以提供最少占用空间的数据库代码,因此查询直接在此处添加。您可以将它们替换为 SP 调用,或将它们移至 constants.cs 文件。
SessDataAccess.cs
包含数据访问代码,其中包含开关,可根据在 web.config 文件中进行的适当配置,在 Oracle 或 MS SQL Server 会话存储之间切换。是的,web.config 文件分别包含 Oracle 和 SQL Server 的两个示例键……您可以根据需要注释掉其中一个。
Oracle 和 MS SQL Server 的数据库脚本
所附代码仅期望数据库中存在一个名为“Sessions
”的 Table
。没有 SP、序列或其他任何内容。Oracle 和 MSSQL Server 的 create table
语句也分别在单独的 .sql 文件中提供。
使用附加代码
创建 SESSIONS 表
您可以选择将应用程序的 session
数据保存在应用程序的数据库中,或者保存在单独的数据库中。创建数据库,然后运行为您的特定数据库 Oracle / Microsoft SQL Server 附加的 CREATE TABLE 脚本。
默认情况下,只有主键,即表的 session
ID varchar
字段将被索引,这对于中等使用的应用程序来说已经足够了。如果需要,您可以为 expiry datetime
字段和 applicationname
字段添加更多索引,因为它们在查询的“where
子句”中最常用。
将类文件复制到 App_code
将文件 CustomSessionStoreProvider.cs 和 SessDataAccess.cs 复制到应用程序的 app_code 文件夹中。
配置 Web.config 中的键
配置您的 Web 应用程序的 web.config,使其具有两个额外的键,这些键在以下位置引用:
<appSettings>
<add key="DBConnectionstring" value="DB_CONNECTION_STRING"/>
<add key="DBProvider" value="Oracle"/>
</appSettings>
键仅从 CustomSessionStoreProvider.cs 文件读取,并且值/设置会更新到 SessDataAccess.cs 的 public static
变量中。
将 DBProvider
键更改为“Oracle
”或“sqlClient
”,具体取决于您使用的是 Oracle 服务器还是 MS Sql Server。
配置会话
添加 SessionState
配置块,为 session
管理的属性提供值。
<sessionState mode="Custom" customProvider="custom_provider"
cookieless="false" timeout="1"><!-- timeout in minutes, 1 min set for testing-->
<providers>
<add name="custom_provider" type="Test.WebSession.CustomSessionStoreProvider"/>
</providers>
</sessionState>
请注意 sessionState
配置中的“customProvider
”属性值,以及通过 <provider>
配置项添加的相应新提供程序,它应该告诉 IIS Web 服务器有关 session
的自定义提供程序……正确指向我们的类 Test.WebSession.CustomSessionStoreProvider
(如果您更改代码中的 Test.WebSession
命名空间,您也应该在此处更改它)。
附加代码中的 Web.config 依赖项
需要 OracleClient .NET Framework 程序集引用……因为它在 SessDataAccess.cs 中被引用。
<compilation debug="true">
<assemblies>
<add assembly="System.Data.OracleClient, Version=2.0.0.0,
Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
</assemblies>
</compilation>
您已完成。使用带有 session
变量的会话。
// Writing to session
Session["userLoggedIn"] = "some username";
Session["userLastLogInDT"] = "2010-08-16 10:00:00";
//Reading from session.
Your last login was at: <%= Response.Write (Session["userLastLogInDT"]); %>
性能测试
我已经在不同的机器上测试了这些源代码,包括 Oracle 10g 和 MSQLserver 2005 及更高版本(32 位和 64 位)。我已将附加代码用于一些中型网站的生产环境,这些网站的命中率接近 1000 用户/秒。
可能的改进
- 通过向 SessDataAccess.cs 添加代码,可以轻松扩展以使用 DB2 / MySQL 或任何数据库引擎
- 可以通过在会话表上添加更多索引来改进,查询可以移植到
StoredProcedures
以进一步提高性能,这在点击率高的应用程序上会很显著 - 可以设置一个镜像数据库作为冗余会话存储
- 归档应用程序可以单独备份
sessions
表上的数据,用于基于会话的报告