我正在使用SQL Server 2008为.NET4应用程序存储会话。会话将存储在[DatabaseA]中。对此没有任何其他自定义配置,因此ASPState数据库完全是开箱即用的方式(使用aspnet_regsql)
我的主应用程序在[DatabaseB](同一服务器)上运行。在此数据库中,我有2个表,这些表记录了一些数据以及sessionID。
通过SQL Agent运行[DeleteExpiredSessions]存储过程(在DatabaseA上)时,已正确地从ASPState中删除了会话,但是我想扩展它以从[DatabaseB]中删除基于SessionID的行。
我尝试编辑[DeleteExpiredSessions]存储过程以包括以下SQL
OPEN ExpiredSessionCursor
FETCH NEXT FROM ExpiredSessionCursor INTO @SessionID
WHILE @@FETCH_STATUS = 0
BEGIN
-- BEGIN MY ADDITIONS
DECLARE @myRecordCount int
SELECT @myRecordCount= COUNT(*) FROM [DatabaseB].dbo.Table1 WHERE [DatabaseB].dbo.Table1.SessionId = @SessionID -- AND [DatabaseB].dbo.Table1.DateEntered < @now
SELECT @myRecordCount
DELETE FROM [DatabaseB].dbo.Table1 WHERE [DatabaseB].dbo.Table1.SessionId = @SessionID AND [DatabaseB].dbo.Table1.DateEntered < @now
DELETE FROM [DatabaseB].dbo.Table2 WHERE [DatabaseB].dbo.Table2.SessionId = @SessionID AND [DatabaseB].dbo.Table2.DateEntered < @now
-- END MY ADDITIONS
DELETE FROM [DatabaseA].dbo.ASPStateTempSessions WHERE SessionID = @SessionID AND Expires < @now
FETCH NEXT FROM ExpiredSessionCursor INTO @SessionID
END
CLOSE ExpiredSessionCursor
DEALLOCATE ExpiredSessionCursor
但是@myRecordCount返回0行。
没有报告错误(代理作业正确运行,并且在SQL Profiler中没有任何错误),并且@myRecordCount在这种情况下应该返回4。
DECLARE / SELECT COUNT作为调试器存在。
更新
因此调试了sql后发现:
SELECT SessionId
FROM [ASPState].dbo.ASPStateTempSessions WITH (READUNCOMMITTED)
WHERE Expires < GETUTCDATE()
--Returns SessionId = '3wj5nyrlz02ezw1vvjts4gjv28d8c075'
SELECT * FROM [DatabaseB].dbo.Table1 -- returns (4) results
SELECT * FROM [DatabaseB].dbo.Table1 WHERE [DatabaseB].dbo.Table1.SessionId = '3wj5nyrlz02ezw1vvjts4gjv28d8c075' -- returns (0) results
我推断出SessionId是错误的。
[DatabaseB].dbo.Table1
中存储的是'3wj5nyrlz02ezw1vvjts4gjv'-请注意字符串的截断。现在,我用于存储会话变量的.NET代码(使用EF6.1.3)是
Table1.SessionId = HttpContext.Current.Session.SessionID
这表示正在存储
3wj5nyrlz02ezw1vvjts4gjv
。Cookie
ASP.NET_SessionId
也具有相同的值。Table1的SessionId列的长度与ASPState tmpSession表的长度相同(88)
更新的问题
有趣的是,ASPStateTempSessions.SessionId变量似乎正在追加
28d8c075
到它的结尾。因此,ASP.NET_SessionId和HttpContext.Current.Session.SessionID为
3wj5nyrlz02ezw1vvjts4gjv
,而ASPStateTempSessions.SessionId为3wj5nyrlz02ezw1vvjts4gjv28d8c075
我刚刚清除了所有会话变量和cookie,并且有了一个新的会话变量,但是28d8c075仍被删除/附加到各种cookie和数据值中。
我知道发生这种情况是因为ASPStateTempSessions将应用程序哈希的后缀附加到SessionId(https://msdn.microsoft.com/en-us/library/aa478952.aspx)
Column Name Column Type Description
SessionId nvarchar(88) Session ID + application ID
如何将其返回到HttpContext.Current.Session.SessionID而不是SessionId?
最佳答案
解决了
问题(正如我最初诊断的那样)是应用程序ID没有传递给SessionID变量。因此,我执行了以下步骤:
1)在Web.Config中创建了一个后备变量(AppName更改的可能性很小)
<add key="AppId" value="685293685"/>
为什么? -这是从ASPState [TempGetAppID]返回的appName的ID
2)在DatabaseB中创建一个存储过程
[GetApplicationIdForSession]
DECLARE @appId int
EXEC [ASPState].dbo.[TempGetAppID]
@appName = @iisName,
@appId = @appId OUTPUT
SELECT @appId as N'AppId'
为什么? -这利用了将AppName哈希到正确的AppId的内置SQL State SP来保持一致性。 (即我没有编写单独的.net函数来尝试获得与SQL相同的值)
3)在Global.asax.vb中
Sub Application_Start(ByVal sender As Object, ByVal e As EventArgs)
Using db As New SqlConnection(ConfigurationManager.ConnectionStrings("LocalServer").ConnectionString)
db.Open()
Using cmd As SqlCommand = db.CreateCommand()
cmd.CommandText = "GetApplicationIdForSession"
cmd.CommandType = CommandType.StoredProcedure
cmd.Parameters.AddWithValue("iisName", HttpRuntime.AppDomainAppId.ToLower)
Using dr As SqlDataReader = cmd.ExecuteReader()
If dr IsNot Nothing AndAlso dr.Read() Then
Application("AppId") = CType(dr("appId"), Integer)
Else
Application("AppId") = CType(ConfigurationManager.AppSettings("AppId"), Integer)
End If
End Using
End Using
db.Close()
End Using
' Fires when the application is started
End Sub
为什么? -HttpRuntime.AppDomainAppId与ASPState数据库(DatabaseA)中使用的值相同。因此我将其传递给在步骤2中创建的存储过程,然后将输出存储在应用程序变量中,这样我就不必每次都访问数据库来获取sessionid。
4)在我的页面继承自的基类中创建此函数
Public Function GetAppIdHash() As String
Dim hashCode As Integer = CType(Application("AppId"), Integer)
Return ((hashCode).ToString("X2")).ToLower
End Function
为什么? -这将获取应用程序变量(“ AppId”)并返回十六进制输出
5)从以下位置更改了存储会话ID变量的.net代码:
Table1.SessionId = HttpContext.Current.Session.SessionID
至
Table1.SessionId = HttpContext.Current.Session.SessionID & GetAppIdHash()
6)
DeleteExpiredSessions
SP已更新为此处列出的-happy medium-版本-http://sqlperformance.com/2013/01/t-sql-queries/optimize-aspstate并附加了其他SQL命令。
ALTER PROCEDURE [dbo].[DeleteExpiredSessions]
@top INT = 1000
AS
BEGIN
SET NOCOUNT ON;
DECLARE @now DATETIME, @c INT;
SELECT @now = GETUTCDATE(), @c = 1;
/* START Additional SQL */
CREATE TABLE #tblExpiredSessions
(
SessionId nvarchar(88) NOT NULL PRIMARY KEY
)
INSERT #tblExpiredSessions (SessionId)
SELECT SessionId
FROM [ASPState].dbo.ASPStateTempSessions WITH (READUNCOMMITTED)
WHERE Expires < @now
/* END Additional SQL */
BEGIN TRANSACTION;
WHILE @c <> 0
BEGIN
;WITH x AS
(
SELECT TOP (@top) SessionId
FROM [ASPState].dbo.ASPStateTempSessions
WHERE Expires < @now
ORDER BY SessionId
)
DELETE x;
SET @c = @@ROWCOUNT;
IF @@TRANCOUNT = 1
BEGIN
COMMIT TRANSACTION;
BEGIN TRANSACTION;
END
END
IF @@TRANCOUNT = 1
BEGIN
COMMIT TRANSACTION;
END
/* START Additional SQL */
DELETE FROM DatabaseB.dbo.Table1 WHERE DatabaseB.dbo.Table2.SessionId in (SELECT * FROM #tblExpiredSessions)
DELETE FROM DatabaseB.dbo.Table2 WHERE DatabaseB.dbo.Table2.SessionId in (SELECT * FROM #tblExpiredSessions)
DROP TABLE #tblExpiredSessions
/* END Additional SQL */
END
旁注-我不是SQL向导,因此如果有人可以提出对
DELETE FROM DatabaseB.dbo.Table1
等部分的改进建议,将不胜感激。 (例如查询中的位置,它是否可以与CTE一起使用),对我来说目前似乎有些笨拙。关于.net - SQL session 状态-数据库SessionId与ASP.NET_SessionID值不同,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/35337237/