diff --git a/src/SqlSessionStateProviderAsync/Microsoft.AspNet.SessionState.SqlSessionStateProviderAsync.csproj b/src/SqlSessionStateProviderAsync/Microsoft.AspNet.SessionState.SqlSessionStateProviderAsync.csproj index a527de7..3b6c90b 100644 --- a/src/SqlSessionStateProviderAsync/Microsoft.AspNet.SessionState.SqlSessionStateProviderAsync.csproj +++ b/src/SqlSessionStateProviderAsync/Microsoft.AspNet.SessionState.SqlSessionStateProviderAsync.csproj @@ -77,6 +77,7 @@ + diff --git a/src/SqlSessionStateProviderAsync/SqlSessionStateProviderAsync.cs b/src/SqlSessionStateProviderAsync/SqlSessionStateProviderAsync.cs index 7a6b3bf..c391274 100644 --- a/src/SqlSessionStateProviderAsync/SqlSessionStateProviderAsync.cs +++ b/src/SqlSessionStateProviderAsync/SqlSessionStateProviderAsync.cs @@ -29,6 +29,7 @@ public class SqlSessionStateProviderAsync : SessionStateStoreProviderAsyncBase private const string RETRY_INTERVAL_CONFIGURATION_NAME = "retryInterval"; private const string CONNECTIONSTRING_NAME_CONFIGURATION_NAME = "connectionStringName"; private const string SESSION_TABLE_CONFIGURATION_NAME = "sessionTableName"; + private const string APP_ID_CONFIGURATION_NAME = "appId"; private const string SESSIONSTATE_SECTION_PATH = "system.web/sessionState"; private const double SessionExpiresFrequencyCheckIntervalTicks = 30 * TimeSpan.TicksPerSecond; private static long s_lastSessionPurgeTicks; @@ -41,7 +42,7 @@ public class SqlSessionStateProviderAsync : SessionStateStoreProviderAsyncBase private static RepositoryType s_repositoryType; private int _rqOrigStreamLen; - + /// /// Initialize the provider through the configuration /// @@ -65,7 +66,7 @@ public override void Initialize(string name, NameValueCollection config) } // for unit tests - internal void Initialize(string name, NameValueCollection config, SessionStateSection ssc, ConnectionStringSettings connectionString, + internal void Initialize(string name, NameValueCollection config, SessionStateSection ssc, ConnectionStringSettings connectionString, bool shouldCreateTable = false) { base.Initialize(name, config); @@ -105,27 +106,52 @@ internal void Initialize(string name, NameValueCollection config, SessionStateSe // Initialize the repository if (s_repositoryType == RepositoryType.InMemory || s_repositoryType == RepositoryType.InMemoryDurable) { - s_sqlSessionStateRepository = new SqlInMemoryTableSessionStateRepository(connectionString.ConnectionString, tableName, - (int)ssc.SqlCommandTimeout.TotalSeconds, GetRetryInterval(config), GetMaxRetryNum(config), (s_repositoryType == RepositoryType.InMemoryDurable)); + s_sqlSessionStateRepository = new SqlInMemoryTableSessionStateRepository( + connectionString.ConnectionString, + tableName, + (int)ssc.SqlCommandTimeout.TotalSeconds, + GetRetryInterval(config), + GetMaxRetryNum(config), + (s_repositoryType == RepositoryType.InMemoryDurable) + ); } else if (s_repositoryType == RepositoryType.FrameworkCompat) { - s_sqlSessionStateRepository = new SqlFxCompatSessionStateRepository(connectionString.ConnectionString, tableName, - (int)ssc.SqlCommandTimeout.TotalSeconds, GetRetryInterval(config), GetMaxRetryNum(config)); + s_sqlSessionStateRepository = new SqlFxCompatSessionStateRepository( + connectionString.ConnectionString, + tableName, + (int)ssc.SqlCommandTimeout.TotalSeconds, + GetRetryInterval(config), + GetMaxRetryNum(config) + ); } else { - s_sqlSessionStateRepository = new SqlSessionStateRepository(connectionString.ConnectionString, tableName, - (int)ssc.SqlCommandTimeout.TotalSeconds, GetRetryInterval(config), GetMaxRetryNum(config)); + s_sqlSessionStateRepository = new SqlSessionStateRepository( + connectionString.ConnectionString, + tableName, + (int)ssc.SqlCommandTimeout.TotalSeconds, + GetRetryInterval(config), + GetMaxRetryNum(config) + ); } if (shouldCreateTable) { s_sqlSessionStateRepository.CreateSessionStateTable(); } - var appId = AppId ?? HttpRuntime.AppDomainAppId; + //============================================================================ robs == + // Use AppId from config if specified, otherwise use AppDomainAppId + //====================================================================== 2023-07-21 == + string configAppId = config[APP_ID_CONFIGURATION_NAME]; + if (string.IsNullOrEmpty(configAppId)) + { + configAppId = HttpRuntime.AppDomainAppId; + } + + string appId = AppId ?? configAppId; Debug.Assert(appId != null); - s_appSuffix = appId.GetHashCode().ToString("X8", CultureInfo.InvariantCulture); + s_appSuffix = appId.GetDeterministicHashCode().ToString("X8", CultureInfo.InvariantCulture); s_oneTimeInited = true; } @@ -188,7 +214,7 @@ internal static Func GetSessionStaticO { int retryInterval; var val = config[RETRY_INTERVAL_CONFIGURATION_NAME]; - if(val != null && int.TryParse(val, out retryInterval)) + if (val != null && int.TryParse(val, out retryInterval)) { return retryInterval; } @@ -214,9 +240,9 @@ public override SessionStateStoreData CreateNewStoreData(HttpContextBase context /// public override async Task CreateUninitializedItemAsync( - HttpContextBase context, - string id, - int timeout, + HttpContextBase context, + string id, + int timeout, CancellationToken cancellationToken) { if (id == null) @@ -259,8 +285,8 @@ public override Task GetItemAsync(HttpContextBase context, string /// public override Task GetItemExclusiveAsync( - HttpContextBase context, - string id, + HttpContextBase context, + string id, CancellationToken cancellationToken) { return DoGet(context, id, true, cancellationToken); @@ -274,9 +300,9 @@ public override void InitializeRequest(HttpContextBase context) /// public override async Task ReleaseItemExclusiveAsync( - HttpContextBase context, - string id, - object lockId, + HttpContextBase context, + string id, + object lockId, CancellationToken cancellationToken) { if (id == null) @@ -295,10 +321,10 @@ public override async Task ReleaseItemExclusiveAsync( /// public override async Task RemoveItemAsync( - HttpContextBase context, - string id, - object lockId, - SessionStateStoreData item, + HttpContextBase context, + string id, + object lockId, + SessionStateStoreData item, CancellationToken cancellationToken) { if (id == null) @@ -317,8 +343,8 @@ public override async Task RemoveItemAsync( /// public override async Task ResetItemTimeoutAsync( - HttpContextBase context, - string id, + HttpContextBase context, + string id, CancellationToken cancellationToken) { if (id == null) @@ -337,11 +363,11 @@ public override async Task ResetItemTimeoutAsync( /// public override async Task SetAndReleaseItemExclusiveAsync( - HttpContextBase context, - string id, - SessionStateStoreData item, - object lockId, - bool newItem, + HttpContextBase context, + string id, + SessionStateStoreData item, + object lockId, + bool newItem, CancellationToken cancellationToken) { byte[] buf; @@ -368,7 +394,7 @@ public override async Task SetAndReleaseItemExclusiveAsync( } catch { - if(!newItem) + if (!newItem) { await ReleaseItemExclusiveAsync(context, id, lockId, cancellationToken); } @@ -393,15 +419,15 @@ private async Task DoGet(HttpContextBase context, string id, bool throw new ArgumentException(SR.Session_id_too_long); } id = AppendAppIdHash(id); - + SessionStateStoreData data = null; var sessionItem = await s_sqlSessionStateRepository.GetSessionStateItemAsync(id, exclusive); - if(sessionItem == null) + if (sessionItem == null) { return null; } - if(sessionItem.Item == null) + if (sessionItem.Item == null) { return new GetItemResult(null, sessionItem.Locked, sessionItem.LockAge, sessionItem.LockId, sessionItem.Actions); } @@ -423,14 +449,14 @@ internal static string AppendAppIdHash(string id) return id + s_appSuffix; } return id; - } + } // Internal code copied from SessionStateUtility internal static void SerializeStoreData( - SessionStateStoreData item, - int initialStreamSize, - out byte[] buf, - out int length, + SessionStateStoreData item, + int initialStreamSize, + out byte[] buf, + out int length, bool compressionEnabled) { using (MemoryStream s = new MemoryStream(initialStreamSize)) @@ -518,7 +544,7 @@ private static SessionStateStoreData Deserialize(HttpContextBase context, Stream try { BinaryReader reader = new BinaryReader(stream); - + timeout = reader.ReadInt32(); hasItems = reader.ReadBoolean(); hasStaticObjects = reader.ReadBoolean(); @@ -527,7 +553,8 @@ private static SessionStateStoreData Deserialize(HttpContextBase context, Stream { sessionItems = SessionStateItemCollection.Deserialize(reader); } - else { + else + { sessionItems = new SessionStateItemCollection(); } @@ -535,7 +562,8 @@ private static SessionStateStoreData Deserialize(HttpContextBase context, Stream { staticObjects = HttpStaticObjectsCollection.Deserialize(reader); } - else { + else + { staticObjects = GetSessionStaticObjects(context.ApplicationInstance.Context); } @@ -600,6 +628,6 @@ private static ConnectionStringSettings GetConnectionString(string connectionstr String.Format(CultureInfo.CurrentCulture, SR.Connection_string_not_found, connectionstringName)); } return conn; - } + } } } diff --git a/src/SqlSessionStateProviderAsync/StringExtensions.cs b/src/SqlSessionStateProviderAsync/StringExtensions.cs new file mode 100644 index 0000000..c498d48 --- /dev/null +++ b/src/SqlSessionStateProviderAsync/StringExtensions.cs @@ -0,0 +1,29 @@ +namespace Microsoft.AspNet.SessionState +{ + internal static class StringExtensions + { + //============================================================================ robs == + // This is to replace the call to GetHashCode() when appending AppId to the session id. + // Using GetHashCode() is not deterministic and can cause problems when used externally (e.g in SQL) + // Credit: https://andrewlock.net/why-is-string-gethashcode-different-each-time-i-run-my-program-in-net-core/ + //====================================================================== 2023-07-21 == + internal static int GetDeterministicHashCode(this string str) + { + unchecked + { + int hash1 = (5381 << 16) + 5381; + int hash2 = hash1; + + for (int i = 0; i < str.Length; i += 2) + { + hash1 = ((hash1 << 5) + hash1) ^ str[i]; + if (i == str.Length - 1) + break; + hash2 = ((hash2 << 5) + hash2) ^ str[i + 1]; + } + + return hash1 + (hash2 * 1566083941); + } + } + } +}