我正在使用SQL Server 2008为.NET4应用程序存储会话。会话将存储在[DatabaseA]中。对此没有任何其他自定义配置,因此ASPState数据库完全是开箱即用的方式(使用aspnet_regsql)
我的主应用程序在[DatabaseB](同一服务器)上运行。在此数据库中,我有2个表,这些表记录了一些数据以及sessionID。
当通过SQL代理运行[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
正在存储该代码。CookieASP.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/zh-cn/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)[GetApplicationIdForSession]
在DatabaseB中创建一个存储过程
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中期版本- 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
etc部分的改进建议,将不胜感激。(例如查询中的位置,它是否可以与CTE一起使用),对我来说目前似乎有些笨拙。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句