Code:
/ DotNET / DotNET / 8.0 / untmp / whidbey / REDBITS / ndp / fx / src / xsp / System / Web / State / sqlstateclientmanager.cs / 1 / sqlstateclientmanager.cs
//------------------------------------------------------------------------------ //// Copyright (c) Microsoft Corporation. All rights reserved. // //----------------------------------------------------------------------------- /* * SqlSessionStateStore.cs * * Copyright (c) 1998-2000, Microsoft Corporation * */ namespace System.Web.SessionState { using System; using System.Configuration; using System.Collections; using System.Threading; using System.IO; using System.Web; using System.Web.Caching; using System.Web.Util; using System.Data; using System.Data.SqlClient; using System.Data.Common; using System.Text; using System.Security.Principal; using System.Xml; using System.Collections.Specialized; using System.Configuration.Provider; using System.Globalization; using System.Web.Management; using System.Web.Hosting; using System.Web.Configuration; /* * Provides session state via SQL Server */ internal class SqlSessionStateStore : SessionStateStoreProviderBase { internal enum SupportFlags : uint { None = 0x00000000, GetLockAge = 0x00000001, Uninitialized = 0xFFFFFFFF } #pragma warning disable 0649 static ReadWriteSpinLock s_lock; #pragma warning restore 0649 static int s_commandTimeout; static SqlPartitionInfo s_singlePartitionInfo; static PartitionManager s_partitionManager; static bool s_oneTimeInited; static bool s_usePartition; static EventHandler s_onAppDomainUnload; // We keep these info because we don't want to hold on to the config object. static string s_configPartitionResolverType; static string s_configSqlConnectionFileName; static int s_configSqlConnectionLineNumber; static bool s_configAllowCustomSqlDatabase; // Per request info HttpContext _rqContext; int _rqOrigStreamLen; IPartitionResolver _partitionResolver; SqlPartitionInfo _partitionInfo; const int ITEM_SHORT_LENGTH = 7000; const int SQL_ERROR_PRIMARY_KEY_VIOLATION = 2627; const int SQL_LOGIN_FAILED = 18456; const int SQL_LOGIN_FAILED_2 = 18452; const int SQL_LOGIN_FAILED_3 = 18450; const int APP_SUFFIX_LENGTH = 8; static int ID_LENGTH = SessionIDManager.SessionIDMaxLength + APP_SUFFIX_LENGTH; internal const int SQL_COMMAND_TIMEOUT_DEFAULT = 30; // in sec internal SqlSessionStateStore() { } internal override void Initialize(string name, NameValueCollection config, IPartitionResolver partitionResolver) { _partitionResolver = partitionResolver; Initialize(name, config); } #if DBG SessionStateModule _module; internal void SetModule(SessionStateModule module) { _module = module; } #endif public override void Initialize(string name, NameValueCollection config) { if (String.IsNullOrEmpty(name)) name = "SQL Server Session State Provider"; base.Initialize(name, config); if (!s_oneTimeInited) { s_lock.AcquireWriterLock(); try { if (!s_oneTimeInited) { OneTimeInit(); } } finally { s_lock.ReleaseWriterLock(); } } if (!s_usePartition) { // For single partition, the connection info won't change from request to request Debug.Assert(_partitionResolver == null); _partitionInfo = s_singlePartitionInfo; } } void OneTimeInit() { SessionStateSection config = RuntimeConfig.GetAppConfig().SessionState; s_configPartitionResolverType = config.PartitionResolverType; s_configSqlConnectionFileName = config.ElementInformation.Properties["sqlConnectionString"].Source; s_configSqlConnectionLineNumber = config.ElementInformation.Properties["sqlConnectionString"].LineNumber; s_configAllowCustomSqlDatabase = config.AllowCustomSqlDatabase; if (_partitionResolver == null) { String sqlConnectionString = config.SqlConnectionString; SessionStateModule.ReadConnectionString(config, ref sqlConnectionString, "sqlConnectionString"); s_singlePartitionInfo = (SqlPartitionInfo)CreatePartitionInfo(sqlConnectionString); } else { s_usePartition = true; s_partitionManager = new PartitionManager(new CreatePartitionInfo(CreatePartitionInfo)); } s_commandTimeout = (int)config.SqlCommandTimeout.TotalSeconds; // We only need to do this in one instance s_onAppDomainUnload = new EventHandler(OnAppDomainUnload); Thread.GetDomain().DomainUnload += s_onAppDomainUnload; // Last thing to set. s_oneTimeInited = true; } void OnAppDomainUnload(Object unusedObject, EventArgs unusedEventArgs) { Debug.Trace("SqlSessionStateStore", "OnAppDomainUnload called"); Thread.GetDomain().DomainUnload -= s_onAppDomainUnload; if (_partitionResolver == null) { if (s_singlePartitionInfo != null) { s_singlePartitionInfo.Dispose(); } } else { if (s_partitionManager != null) { s_partitionManager.Dispose(); } } } internal IPartitionInfo CreatePartitionInfo(string sqlConnectionString) { /* * Parse the connection string for errors. We want to ensure * that the user's connection string doesn't contain an * Initial Catalog entry, so we must first create a dummy connection. */ SqlConnection dummyConnection; string attachDBFilename = null; try { dummyConnection = new SqlConnection(sqlConnectionString); } catch (Exception e) { if (s_usePartition) { HttpException outerException = new HttpException( SR.GetString(SR.Error_parsing_sql_partition_resolver_string, s_configPartitionResolverType, e.Message), e); outerException.SetFormatter(new UseLastUnhandledErrorFormatter(outerException)); throw outerException; } else { throw new ConfigurationErrorsException( SR.GetString(SR.Error_parsing_session_sqlConnectionString, e.Message), e, s_configSqlConnectionFileName, s_configSqlConnectionLineNumber); } } // Search for both Database and AttachDbFileName. Don't append our // database name if either of them exists. string database = dummyConnection.Database; SqlConnectionStringBuilder scsb = new SqlConnectionStringBuilder(sqlConnectionString); if (String.IsNullOrEmpty(database)) { database = scsb.AttachDBFilename; attachDBFilename = database; } if (!String.IsNullOrEmpty(database)) { if (!s_configAllowCustomSqlDatabase) { if (s_usePartition) { throw new HttpException( SR.GetString(SR.No_database_allowed_in_sql_partition_resolver_string, s_configPartitionResolverType, dummyConnection.DataSource, database)); } else { throw new ConfigurationErrorsException( SR.GetString(SR.No_database_allowed_in_sqlConnectionString), s_configSqlConnectionFileName, s_configSqlConnectionLineNumber); } } if (attachDBFilename != null) { HttpRuntime.CheckFilePermission(attachDBFilename, true); } } else { sqlConnectionString += ";Initial Catalog=ASPState"; } return new SqlPartitionInfo(new ResourcePool(new TimeSpan(0, 0, 5), int.MaxValue), scsb.IntegratedSecurity, sqlConnectionString); } public override bool SetItemExpireCallback(SessionStateItemExpireCallback expireCallback) { return false; } public override void Dispose() { } public override void InitializeRequest(HttpContext context) { Debug.Assert(context != null, "context != null"); _rqContext = context; _rqOrigStreamLen = 0; if (s_usePartition) { // For multiple partition case, the connection info can change from request to request _partitionInfo = null; } } public override void EndRequest(HttpContext context) { Debug.Assert(context != null, "context != null"); _rqContext = null; } public bool KnowForSureNotUsingIntegratedSecurity { get { if (_partitionInfo == null) { Debug.Assert(s_usePartition, "_partitionInfo can be null only if we're using paritioning and we haven't called GetConnection yet."); // If we're using partitioning, we need the session id to figure out the connection // string. Without it, we can't know for sure. return false; } else { Debug.Assert(_partitionInfo != null); return !_partitionInfo.UseIntegratedSecurity; } } } // // Regarding resource pool, we will turn it on if in: // - User is not using integrated security // - impersonation = "false" // - impersonation = "true" and userName/password is NON-null // - impersonation = "true" and IIS is using Anonymous // // Otherwise, the impersonated account will be dynamic and we have to turn // resource pooling off. // // Note: // In case 2. above, the user can specify different usernames in different // web.config in different subdirs in the app. In this case, we will just // cache the connections in the resource pool based on the identity of the // connection. So in this specific scenario it is possible to have the // resource pool filled with mixed identities. // bool CanUsePooling() { bool ret; if (KnowForSureNotUsingIntegratedSecurity) { Debug.Trace("SessionStatePooling", "CanUsePooling: not using integrated security"); ret = true; } else if (_rqContext == null) { // One way this can happen is we hit an error on page compilation, // and SessionStateModule.OnEndRequest is called Debug.Trace("SessionStatePooling", "CanUsePooling: no context"); ret = false; } else if (!_rqContext.IsClientImpersonationConfigured) { Debug.Trace("SessionStatePooling", "CanUsePooling: mode is None or Application"); ret = true; } else if (HttpRuntime.IsOnUNCShareInternal) { Debug.Trace("SessionStatePooling", "CanUsePooling: mode is UNC"); ret = false; } else { string logon = _rqContext.WorkerRequest.GetServerVariable("LOGON_USER"); Debug.Trace("SessionStatePooling", "LOGON_USER = '" + logon + "'; identity = '" + _rqContext.User.Identity.Name + "'; IsUNC = " + HttpRuntime.IsOnUNCShareInternal); if (String.IsNullOrEmpty(logon)) { ret = true; } else { ret = false; } } Debug.Trace("SessionStatePooling", "CanUsePooling returns " + ret); return ret; } SqlStateConnection GetConnection(string id, ref bool usePooling) { SqlStateConnection conn = null; if (_partitionInfo == null) { Debug.Assert(s_partitionManager != null); Debug.Assert(_partitionResolver != null); _partitionInfo = (SqlPartitionInfo)s_partitionManager.GetPartition(_partitionResolver, id); } Debug.Trace("SessionStatePooling", "Calling GetConnection under " + WindowsIdentity.GetCurrent().Name); #if DBG Debug.Assert(_module._rqChangeImpersonationRefCount != 0, "SessionStateModule.ChangeImpersonation should have been called before making any call to SQL"); #endif usePooling = CanUsePooling(); if (usePooling) { conn = (SqlStateConnection) _partitionInfo.RetrieveResource(); if (conn != null && (conn.Connection.State & ConnectionState.Open) == 0) { conn.Dispose(); conn = null; } } if (conn == null) { conn = new SqlStateConnection(_partitionInfo); } return conn; } void DisposeOrReuseConnection(ref SqlStateConnection conn, bool usePooling) { try { if (conn == null) { return; } if (usePooling) { _partitionInfo.StoreResource(conn); conn = null; } } finally { if (conn != null) { conn.Dispose(); } } } internal static void ThrowSqlConnectionException(SqlConnection conn, Exception e) { if (s_usePartition) { throw new HttpException( SR.GetString(SR.Cant_connect_sql_session_database_partition_resolver, s_configPartitionResolverType, conn.DataSource, conn.Database)); } else { throw new HttpException( SR.GetString(SR.Cant_connect_sql_session_database), e); } } SessionStateStoreData DoGet(HttpContext context, String id, bool getExclusive, out bool locked, out TimeSpan lockAge, out object lockId, out SessionStateActions actionFlags) { SqlDataReader reader; byte [] buf; MemoryStream stream = null; SessionStateStoreData item; bool useGetLockAge = false; SqlStateConnection conn = null; SqlCommand cmd = null; bool usePooling = true; Debug.Assert(id.Length <= SessionIDManager.SESSION_ID_LENGTH_LIMIT, "id.Length <= SessionIDManager.SESSION_ID_LENGTH_LIMIT"); Debug.Assert(context != null, "context != null"); // Set default return values locked = false; lockId = null; lockAge = TimeSpan.Zero; actionFlags = 0; buf = null; reader = null; conn = GetConnection(id, ref usePooling); Debug.Assert(_partitionInfo != null, "_partitionInfo != null"); Debug.Assert(_partitionInfo.SupportFlags != SupportFlags.Uninitialized, "_partitionInfo.SupportFlags != SupportFlags.Uninitialized"); // // In general, if we're talking to a SQL 2000 or above, we use LockAge; otherwise we use LockDate. // Below are the details: // // Version 1 // --------- // In v1, the lockDate is generated and stored in SQL using local time, and we calculate the "lockage" // (i.e. how long the item is locked) by having the web server read lockDate from SQL and substract it // from DateTime.Now. But this approach introduced two problems: // 1. SQL server and web servers need to be in the same time zone. // 2. Daylight savings problem. // // Version 1.1 // ----------- // In v1.1, if using SQL 2000 we fixed the problem by calculating the "lockage" directly in SQL // so that the SQL server and the web server don't have to be in the same time zone. We also // use UTC date to store time in SQL so that the Daylight savings problem is solved. // // In summary, if using SQL 2000 we made the following changes to the SQL tables: // i. The column Expires is using now UTC time // ii. Add new SP TempGetStateItem2 and TempGetStateItemExclusive2 to return a lockage // instead of a lockDate. // iii. To support v1 web server, we still need to have TempGetStateItem and // TempGetStateItemExclusive. However, we modify it a bit so that they use // UTC time to update Expires column. // // If using SQL 7, we decided not to fix the problem, and the SQL scripts for SQL 7 remain pretty much // the same. That means v1.1 web server will continue to call TempGetStateItem and // TempGetStateItemExclusive and use v1 way to calculate the "lockage". // // Version 2.0 // ----------- // In v2.0 we added some new SP TempGetStateItem3 and TempGetStateItemExclusive3 // because we added a new return value 'actionFlags'. However, the principle remains the same // that we support lockAge only if talking to SQL 2000. // // (When one day MS stops supporting SQL 7 we can remove all the SQL7-specific scripts and // stop all these craziness.) // if ((_partitionInfo.SupportFlags & SupportFlags.GetLockAge) != 0) { useGetLockAge = true; } try { if (getExclusive) { cmd = conn.TempGetExclusive; } else { cmd = conn.TempGet; } cmd.Parameters[0].Value = id + _partitionInfo.AppSuffix; // @id cmd.Parameters[1].Value = Convert.DBNull; // @itemShort cmd.Parameters[2].Value = Convert.DBNull; // @locked cmd.Parameters[3].Value = Convert.DBNull; // @lockDate or @lockAge cmd.Parameters[4].Value = Convert.DBNull; // @lockCookie cmd.Parameters[5].Value = Convert.DBNull; // @actionFlags try { reader = cmd.ExecuteReader(); /* If the cmd returned data, we must read it all before getting out params */ if (reader != null) { try { if (reader.Read()) { Debug.Trace("SqlSessionStateStore", "Sql Get returned long item"); buf = (byte[]) reader[0]; } } finally { reader.Close(); } } } catch (Exception e) { ThrowSqlConnectionException(cmd.Connection, e); } /* Check if value was returned */ if (Convert.IsDBNull(cmd.Parameters[2].Value)) { Debug.Trace("SqlSessionStateStore", "Sql Get returned null"); return null; } /* Check if item is locked */ Debug.Assert(!Convert.IsDBNull(cmd.Parameters[3].Value), "!Convert.IsDBNull(cmd.Parameters[3].Value)"); Debug.Assert(!Convert.IsDBNull(cmd.Parameters[4].Value), "!Convert.IsDBNull(cmd.Parameters[4].Value)"); locked = (bool) cmd.Parameters[2].Value; lockId = (int) cmd.Parameters[4].Value; if (locked) { Debug.Trace("SqlSessionStateStore", "Sql Get returned item that was locked"); Debug.Assert(((int)cmd.Parameters[5].Value & (int)SessionStateActions.InitializeItem) == 0, "(cmd.Parameters[5].Value & SessionStateActions.InitializeItem) == 0; uninit item shouldn't be locked"); if (useGetLockAge) { lockAge = new TimeSpan(0, 0, (int) cmd.Parameters[3].Value); } else { DateTime lockDate; lockDate = (DateTime) cmd.Parameters[3].Value; lockAge = DateTime.Now - lockDate; } Debug.Trace("SqlSessionStateStore", "LockAge = " + lockAge); if (lockAge > new TimeSpan(0, 0, Sec.ONE_YEAR)) { Debug.Trace("SqlSessionStateStore", "Lock age is more than 1 year!!!"); lockAge = TimeSpan.Zero; } return null; } actionFlags = (SessionStateActions) cmd.Parameters[5].Value; if (buf == null) { /* Get short item */ Debug.Assert(!Convert.IsDBNull(cmd.Parameters[1].Value), "!Convert.IsDBNull(cmd.Parameters[1].Value)"); Debug.Trace("SqlSessionStateStore", "Sql Get returned short item"); buf = (byte[]) cmd.Parameters[1].Value; Debug.Assert(buf != null, "buf != null"); } // Done with the connection. DisposeOrReuseConnection(ref conn, usePooling); try { stream = new MemoryStream(buf); item = SessionStateUtility.Deserialize(context, stream); _rqOrigStreamLen = (int) stream.Position; } finally { if (stream != null) { stream.Close(); } } return item; } finally { DisposeOrReuseConnection(ref conn, usePooling); } } public override SessionStateStoreData GetItem(HttpContext context, String id, out bool locked, out TimeSpan lockAge, out object lockId, out SessionStateActions actionFlags) { Debug.Trace("SqlSessionStateStore", "Calling Sql Get, id=" + id); SessionIDManager.CheckIdLength(id, true /* throwOnFail */); return DoGet(context, id, false, out locked, out lockAge, out lockId, out actionFlags); } public override SessionStateStoreData GetItemExclusive(HttpContext context, String id, out bool locked, out TimeSpan lockAge, out object lockId, out SessionStateActions actionFlags) { Debug.Trace("SqlSessionStateStore", "Calling Sql GetExclusive, id=" + id); SessionIDManager.CheckIdLength(id, true /* throwOnFail */); return DoGet(context, id, true, out locked, out lockAge, out lockId, out actionFlags); } public override void ReleaseItemExclusive(HttpContext context, String id, object lockId) { Debug.Trace("SqlSessionStateStore", "Calling Sql ReleaseExclusive, id=" + id); Debug.Assert(lockId != null, "lockId != null"); Debug.Assert(context != null, "context != null"); bool usePooling = true; SqlStateConnection conn = null; int lockCookie = (int)lockId; try { SessionIDManager.CheckIdLength(id, true /* throwOnFail */); conn = GetConnection(id, ref usePooling); try { SqlCommand cmd = conn.TempReleaseExclusive; cmd.Parameters[0].Value = id + _partitionInfo.AppSuffix; cmd.Parameters[1].Value = lockCookie; cmd.ExecuteNonQuery(); } catch (Exception e) { ThrowSqlConnectionException(conn.Connection, e); } } finally { DisposeOrReuseConnection(ref conn, usePooling); } } public override void SetAndReleaseItemExclusive(HttpContext context, String id, SessionStateStoreData item, object lockId, bool newItem) { byte [] buf; int length; SqlCommand cmd; bool usePooling = true; SqlStateConnection conn = null; int lockCookie; Debug.Assert(context != null, "context != null"); try { Debug.Trace("SqlSessionStateStore", "Calling Sql Set, id=" + id); Debug.Assert(item.Items != null, "item.Items != null"); Debug.Assert(item.StaticObjects != null, "item.StaticObjects != null"); SessionIDManager.CheckIdLength(id, true /* throwOnFail */); try { SessionStateUtility.SerializeStoreData(item, ITEM_SHORT_LENGTH, out buf, out length); } catch { if (!newItem) { ((SessionStateStoreProviderBase)this).ReleaseItemExclusive(context, id, lockId); } throw; } // Save it to the store if (lockId == null) { lockCookie = 0; } else { lockCookie = (int)lockId; } conn = GetConnection(id, ref usePooling); if (!newItem) { Debug.Assert(_rqOrigStreamLen > 0, "_rqOrigStreamLen > 0"); if (length <= ITEM_SHORT_LENGTH) { if (_rqOrigStreamLen <= ITEM_SHORT_LENGTH) { cmd = conn.TempUpdateShort; } else { cmd = conn.TempUpdateShortNullLong; } } else { if (_rqOrigStreamLen <= ITEM_SHORT_LENGTH) { cmd = conn.TempUpdateLongNullShort; } else { cmd = conn.TempUpdateLong; } } } else { if (length <= ITEM_SHORT_LENGTH) { cmd = conn.TempInsertShort; } else { cmd = conn.TempInsertLong; } } cmd.Parameters[0].Value = id + _partitionInfo.AppSuffix; cmd.Parameters[1].Size = length; cmd.Parameters[1].Value = buf; cmd.Parameters[2].Value = item.Timeout; if (!newItem) { cmd.Parameters[3].Value = lockCookie; } try { cmd.ExecuteNonQuery(); } catch (Exception e) { HandleInsertException(conn.Connection, e, newItem, id); } } finally { DisposeOrReuseConnection(ref conn, usePooling); } } public override void RemoveItem(HttpContext context, String id, object lockId, SessionStateStoreData item) { Debug.Trace("SqlSessionStateStore", "Calling Sql Remove, id=" + id); Debug.Assert(lockId != null, "lockId != null"); Debug.Assert(context != null, "context != null"); bool usePooling = true; SqlStateConnection conn = null; int lockCookie = (int)lockId; try { SessionIDManager.CheckIdLength(id, true /* throwOnFail */); conn = GetConnection(id, ref usePooling); try { SqlCommand cmd = conn.TempRemove; cmd.Parameters[0].Value = id + _partitionInfo.AppSuffix; cmd.Parameters[1].Value = lockCookie; cmd.ExecuteNonQuery(); } catch (Exception e) { ThrowSqlConnectionException(conn.Connection, e); } } finally { DisposeOrReuseConnection(ref conn, usePooling); } } public override void ResetItemTimeout(HttpContext context, String id) { Debug.Trace("SqlSessionStateStore", "Calling Sql ResetTimeout, id=" + id); Debug.Assert(context != null, "context != null"); bool usePooling = true; SqlStateConnection conn = null; try { SessionIDManager.CheckIdLength(id, true /* throwOnFail */); conn = GetConnection(id, ref usePooling); try { SqlCommand cmd = conn.TempResetTimeout; cmd.Parameters[0].Value = id + _partitionInfo.AppSuffix; cmd.ExecuteNonQuery(); } catch (Exception e) { ThrowSqlConnectionException(conn.Connection, e); } } finally { DisposeOrReuseConnection(ref conn, usePooling); } } public override SessionStateStoreData CreateNewStoreData(HttpContext context, int timeout) { Debug.Assert(context != null, "context != null"); return SessionStateUtility.CreateLegitStoreData(context, null, null, timeout); } public override void CreateUninitializedItem(HttpContext context, String id, int timeout) { Debug.Trace("SqlSessionStateStore", "Calling Sql InsertUninitializedItem, id=" + id); Debug.Assert(context != null, "context != null"); bool usePooling = true; SqlStateConnection conn = null; byte [] buf; int length; try { SessionIDManager.CheckIdLength(id, true /* throwOnFail */); // Store an empty data SessionStateUtility.SerializeStoreData(CreateNewStoreData(context, timeout), ITEM_SHORT_LENGTH, out buf, out length); conn = GetConnection(id, ref usePooling); try { SqlCommand cmd = conn.TempInsertUninitializedItem; cmd.Parameters[0].Value = id + _partitionInfo.AppSuffix; cmd.Parameters[1].Size = length; cmd.Parameters[1].Value = buf; cmd.Parameters[2].Value = timeout; cmd.ExecuteNonQuery(); } catch (Exception e) { HandleInsertException(conn.Connection, e, true, id); } } finally { DisposeOrReuseConnection(ref conn, usePooling); } } void HandleInsertException(SqlConnection conn, Exception e, bool newItem, string id) { SqlException sqlExpt = e as SqlException; if (sqlExpt != null && sqlExpt.Number == SQL_ERROR_PRIMARY_KEY_VIOLATION && newItem) { Debug.Trace("SessionStateClientSet", "Insert failed because of primary key violation; just leave gracefully; id=" + id); // It's possible that two threads (from the same session) are creating the session // state, both failed to get it first, and now both tried to insert it. // One thread may lose with a Primary Key Violation error. If so, that thread will // just lose and exit gracefully. } else { ThrowSqlConnectionException(conn, e); } } internal class SqlPartitionInfo : PartitionInfo { bool _useIntegratedSecurity; string _sqlConnectionString; string _tracingPartitionString; SupportFlags _support = SupportFlags.Uninitialized; string _appSuffix; object _lock = new object(); bool _sqlInfoInited; const string APP_SUFFIX_FORMAT = "x8"; const int APPID_MAX = 280; const int SQL_2000_MAJ_VER = 8; internal SqlPartitionInfo(ResourcePool rpool, bool useIntegratedSecurity, string sqlConnectionString) : base(rpool) { _useIntegratedSecurity = useIntegratedSecurity; _sqlConnectionString = sqlConnectionString; Debug.Trace("PartitionInfo", "Created a new info, sqlConnectionString=" + sqlConnectionString); } internal bool UseIntegratedSecurity { get { return _useIntegratedSecurity; } } internal string SqlConnectionString { get { return _sqlConnectionString; } } internal SupportFlags SupportFlags { get { return _support; } set { _support = value; } } protected override string TracingPartitionString { get { if (_tracingPartitionString == null) { SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder(_sqlConnectionString); builder.Password = String.Empty; builder.UserID = String.Empty; _tracingPartitionString = builder.ConnectionString; } return _tracingPartitionString; } } internal string AppSuffix { get { return _appSuffix; } } void GetServerSupportOptions(SqlConnection sqlConnection) { Debug.Assert(SupportFlags == SupportFlags.Uninitialized); SqlCommand cmd; SqlDataReader reader = null; SupportFlags flags = SupportFlags.None; bool v2 = false; SqlParameter p; // First, check if the SQL server is running Whidbey scripts cmd = new SqlCommand("Select name from sysobjects where type = 'P' and name = 'TempGetVersion'", sqlConnection); cmd.CommandType = CommandType.Text; try { reader = cmd.ExecuteReader(CommandBehavior.SingleRow); if (reader.Read()) { // This function first appears in Whidbey (v2). So we know it's // at least 2.0 even without reading its content. v2 = true; } } catch (Exception e) { SqlSessionStateStore.ThrowSqlConnectionException(sqlConnection, e); } finally { if (reader != null) { reader.Close(); reader = null; } } if (!v2) { if (s_usePartition) { throw new HttpException( SR.GetString(SR.Need_v2_SQL_Server_partition_resolver, s_configPartitionResolverType, sqlConnection.DataSource, sqlConnection.Database)); } else { throw new HttpException( SR.GetString(SR.Need_v2_SQL_Server)); } } // Then, see if it's SQL 2000 or above cmd = new SqlCommand("dbo.GetMajorVersion", sqlConnection); cmd.CommandType = CommandType.StoredProcedure; p = cmd.Parameters.Add(new SqlParameter("@@ver", SqlDbType.Int)); p.Direction = ParameterDirection.Output; try { cmd.ExecuteNonQuery(); if ((int)p.Value >= SQL_2000_MAJ_VER) { // For details, see the extensive doc in DoGet method. flags |= SupportFlags.GetLockAge; } Debug.Trace("PartitionInfo", "SupportFlags initialized to " + flags); SupportFlags = flags; } catch (Exception e) { SqlSessionStateStore.ThrowSqlConnectionException(sqlConnection, e); } } internal void InitSqlInfo(SqlConnection sqlConnection) { if (_sqlInfoInited) { return; } lock (_lock) { if (_sqlInfoInited) { return; } GetServerSupportOptions(sqlConnection); // Get AppSuffix info SqlParameter p; SqlCommand cmdTempGetAppId = new SqlCommand("dbo.TempGetAppID", sqlConnection); cmdTempGetAppId.CommandType = CommandType.StoredProcedure; cmdTempGetAppId.CommandTimeout = s_commandTimeout; // AppDomainAppIdInternal will contain the whole metabase path of the request's app // e.g. /lm/w3svc/1/root/fxtest p = cmdTempGetAppId.Parameters.Add(new SqlParameter("@appName", SqlDbType.VarChar, APPID_MAX)); p.Value = HttpRuntime.AppDomainAppIdInternal; p = cmdTempGetAppId.Parameters.Add(new SqlParameter("@appId", SqlDbType.Int)); p.Direction = ParameterDirection.Output; p.Value = Convert.DBNull; cmdTempGetAppId.ExecuteNonQuery(); Debug.Assert(!Convert.IsDBNull(p), "!Convert.IsDBNull(p)"); int appId = (int) p.Value; _appSuffix = (appId).ToString(APP_SUFFIX_FORMAT, CultureInfo.InvariantCulture); _sqlInfoInited = true; } } }; /* Here are all the sprocs created for session state and how they're used: CreateTempTables - Called during setup DeleteExpiredSessions - Called by SQL agent to remove expired sessions GetHashCode - Called by sproc TempGetAppID GetMajorVersion - Called during setup TempGetAppID - Called when an asp.net application starts up TempGetStateItem - Used for ReadOnly session state - Called by v1 asp.net - Called by v1.1 asp.net against SQL 7 TempGetStateItem2 - Used for ReadOnly session state - Called by v1.1 asp.net against SQL 2000 TempGetStateItem3 - Used for ReadOnly session state - Called by v2 asp.net TempGetStateItemExclusive - Called by v1 asp.net - Called by v1.1 asp.net against SQL 7 TempGetStateItemExclusive2 - Called by v1.1 asp.net against SQL 2000 TempGetStateItemExclusive3 - Called by v2 asp.net TempGetVersion - Called by v2 asp.net when an application starts up TempInsertStateItemLong - Used when creating a new session state with size > 7000 bytes TempInsertStateItemShort - Used when creating a new session state with size <= 7000 bytes TempInsertUninitializedItem - Used when creating a new uninitilized session state (cookieless="true" and regenerateExpiredSessionId="true" in config) TempReleaseStateItemExclusive - Used when a request that has acquired the session state (exclusively) hit an error during the page execution TempRemoveStateItem - Used when a session is abandoned TempResetTimeout - Used when a request (with an active session state) is handled by an HttpHandler which doesn't support IRequiresSessionState interface. TempUpdateStateItemLong - Used when updating a session state with size > 7000 bytes TempUpdateStateItemLongNullShort - Used when updating a session state where original size <= 7000 bytes but new size > 7000 bytes TempUpdateStateItemShort - Used when updating a session state with size <= 7000 bytes TempUpdateStateItemShortNullLong - Used when updating a session state where original size > 7000 bytes but new size <= 7000 bytes */ class SqlStateConnection : IDisposable { SqlConnection _sqlConnection; SqlCommand _cmdTempGet; SqlCommand _cmdTempGetExclusive; SqlCommand _cmdTempReleaseExclusive; SqlCommand _cmdTempInsertShort; SqlCommand _cmdTempInsertLong; SqlCommand _cmdTempUpdateShort; SqlCommand _cmdTempUpdateShortNullLong; SqlCommand _cmdTempUpdateLong; SqlCommand _cmdTempUpdateLongNullShort; SqlCommand _cmdTempRemove; SqlCommand _cmdTempResetTimeout; SqlCommand _cmdTempInsertUninitializedItem; SqlPartitionInfo _partitionInfo; internal SqlStateConnection(SqlPartitionInfo sqlPartitionInfo) { Debug.Trace("SessionStateConnectionIdentity", "Connecting under " + WindowsIdentity.GetCurrent().Name); _partitionInfo = sqlPartitionInfo; _sqlConnection = new SqlConnection(sqlPartitionInfo.SqlConnectionString); try { _sqlConnection.Open(); } catch (Exception e) { SqlConnection connection = _sqlConnection; SqlException sqlExpt = e as SqlException; _sqlConnection = null; if (sqlExpt != null && (sqlExpt.Number == SQL_LOGIN_FAILED || sqlExpt.Number == SQL_LOGIN_FAILED_2 || sqlExpt.Number == SQL_LOGIN_FAILED_3)) { string user; SqlConnectionStringBuilder scsb = new SqlConnectionStringBuilder(sqlPartitionInfo.SqlConnectionString); if (scsb.IntegratedSecurity) { user = WindowsIdentity.GetCurrent().Name; } else { user = scsb.UserID; } HttpException outerException = new HttpException( SR.GetString(SR.Login_failed_sql_session_database, user ), e); outerException.SetFormatter(new UseLastUnhandledErrorFormatter(outerException)); e = outerException; } SqlSessionStateStore.ThrowSqlConnectionException(connection, e); } try { _partitionInfo.InitSqlInfo(_sqlConnection); Debug.Assert(sqlPartitionInfo.SupportFlags != SupportFlags.Uninitialized); PerfCounters.IncrementCounter(AppPerfCounter.SESSION_SQL_SERVER_CONNECTIONS); } catch { Dispose(); throw; } } internal SqlCommand TempGet { get { if (_cmdTempGet == null) { SqlParameter p; _cmdTempGet = new SqlCommand("dbo.TempGetStateItem3", _sqlConnection); _cmdTempGet.CommandType = CommandType.StoredProcedure; _cmdTempGet.CommandTimeout = s_commandTimeout; // Use a different set of parameters for the sprocs that support GetLockAge if ((_partitionInfo.SupportFlags & SupportFlags.GetLockAge) != 0) { _cmdTempGet.Parameters.Add(new SqlParameter("@id", SqlDbType.NVarChar, ID_LENGTH)); p = _cmdTempGet.Parameters.Add(new SqlParameter("@itemShort", SqlDbType.VarBinary, ITEM_SHORT_LENGTH)); p.Direction = ParameterDirection.Output; p = _cmdTempGet.Parameters.Add(new SqlParameter("@locked", SqlDbType.Bit)); p.Direction = ParameterDirection.Output; p = _cmdTempGet.Parameters.Add(new SqlParameter("@lockAge", SqlDbType.Int)); p.Direction = ParameterDirection.Output; p = _cmdTempGet.Parameters.Add(new SqlParameter("@lockCookie", SqlDbType.Int)); p.Direction = ParameterDirection.Output; p = _cmdTempGet.Parameters.Add(new SqlParameter("@actionFlags", SqlDbType.Int)); p.Direction = ParameterDirection.Output; } else { _cmdTempGet.Parameters.Add(new SqlParameter("@id", SqlDbType.NVarChar, ID_LENGTH)); p = _cmdTempGet.Parameters.Add(new SqlParameter("@itemShort", SqlDbType.VarBinary, ITEM_SHORT_LENGTH)); p.Direction = ParameterDirection.Output; p = _cmdTempGet.Parameters.Add(new SqlParameter("@locked", SqlDbType.Bit)); p.Direction = ParameterDirection.Output; p = _cmdTempGet.Parameters.Add(new SqlParameter("@lockDate", SqlDbType.DateTime)); p.Direction = ParameterDirection.Output; p = _cmdTempGet.Parameters.Add(new SqlParameter("@lockCookie", SqlDbType.Int)); p.Direction = ParameterDirection.Output; p = _cmdTempGet.Parameters.Add(new SqlParameter("@actionFlags", SqlDbType.Int)); p.Direction = ParameterDirection.Output; } } return _cmdTempGet; } } internal SqlCommand TempGetExclusive { get { if (_cmdTempGetExclusive == null) { SqlParameter p; _cmdTempGetExclusive = new SqlCommand("dbo.TempGetStateItemExclusive3", _sqlConnection); _cmdTempGetExclusive.CommandType = CommandType.StoredProcedure; _cmdTempGetExclusive.CommandTimeout = s_commandTimeout; // Use a different set of parameters for the sprocs that support GetLockAge if ((_partitionInfo.SupportFlags & SupportFlags.GetLockAge) != 0) { _cmdTempGetExclusive.Parameters.Add(new SqlParameter("@id", SqlDbType.NVarChar, ID_LENGTH)); p = _cmdTempGetExclusive.Parameters.Add(new SqlParameter("@itemShort", SqlDbType.VarBinary, ITEM_SHORT_LENGTH)); p.Direction = ParameterDirection.Output; p = _cmdTempGetExclusive.Parameters.Add(new SqlParameter("@locked", SqlDbType.Bit)); p.Direction = ParameterDirection.Output; p = _cmdTempGetExclusive.Parameters.Add(new SqlParameter("@lockAge", SqlDbType.Int)); p.Direction = ParameterDirection.Output; p = _cmdTempGetExclusive.Parameters.Add(new SqlParameter("@lockCookie", SqlDbType.Int)); p.Direction = ParameterDirection.Output; p = _cmdTempGetExclusive.Parameters.Add(new SqlParameter("@actionFlags", SqlDbType.Int)); p.Direction = ParameterDirection.Output; } else { _cmdTempGetExclusive.Parameters.Add(new SqlParameter("@id", SqlDbType.NVarChar, ID_LENGTH)); p = _cmdTempGetExclusive.Parameters.Add(new SqlParameter("@itemShort", SqlDbType.VarBinary, ITEM_SHORT_LENGTH)); p.Direction = ParameterDirection.Output; p = _cmdTempGetExclusive.Parameters.Add(new SqlParameter("@locked", SqlDbType.Bit)); p.Direction = ParameterDirection.Output; p = _cmdTempGetExclusive.Parameters.Add(new SqlParameter("@lockDate", SqlDbType.DateTime)); p.Direction = ParameterDirection.Output; p = _cmdTempGetExclusive.Parameters.Add(new SqlParameter("@lockCookie", SqlDbType.Int)); p.Direction = ParameterDirection.Output; p = _cmdTempGetExclusive.Parameters.Add(new SqlParameter("@actionFlags", SqlDbType.Int)); p.Direction = ParameterDirection.Output; } } return _cmdTempGetExclusive; } } internal SqlCommand TempReleaseExclusive { get { if (_cmdTempReleaseExclusive == null) { /* ReleaseExlusive */ _cmdTempReleaseExclusive = new SqlCommand("dbo.TempReleaseStateItemExclusive", _sqlConnection); _cmdTempReleaseExclusive.CommandType = CommandType.StoredProcedure; _cmdTempReleaseExclusive.CommandTimeout = s_commandTimeout; _cmdTempReleaseExclusive.Parameters.Add(new SqlParameter("@id", SqlDbType.NVarChar, ID_LENGTH)); _cmdTempReleaseExclusive.Parameters.Add(new SqlParameter("@lockCookie", SqlDbType.Int)); } return _cmdTempReleaseExclusive; } } internal SqlCommand TempInsertLong { get { if (_cmdTempInsertLong == null) { _cmdTempInsertLong = new SqlCommand("dbo.TempInsertStateItemLong", _sqlConnection); _cmdTempInsertLong.CommandType = CommandType.StoredProcedure; _cmdTempInsertLong.CommandTimeout = s_commandTimeout; _cmdTempInsertLong.Parameters.Add(new SqlParameter("@id", SqlDbType.NVarChar, ID_LENGTH)); _cmdTempInsertLong.Parameters.Add(new SqlParameter("@itemLong", SqlDbType.Image, 8000)); _cmdTempInsertLong.Parameters.Add(new SqlParameter("@timeout", SqlDbType.Int)); } return _cmdTempInsertLong; } } internal SqlCommand TempInsertShort { get { /* Insert */ if (_cmdTempInsertShort == null) { _cmdTempInsertShort = new SqlCommand("dbo.TempInsertStateItemShort", _sqlConnection); _cmdTempInsertShort.CommandType = CommandType.StoredProcedure; _cmdTempInsertShort.CommandTimeout = s_commandTimeout; _cmdTempInsertShort.Parameters.Add(new SqlParameter("@id", SqlDbType.NVarChar, ID_LENGTH)); _cmdTempInsertShort.Parameters.Add(new SqlParameter("@itemShort", SqlDbType.VarBinary, ITEM_SHORT_LENGTH)); _cmdTempInsertShort.Parameters.Add(new SqlParameter("@timeout", SqlDbType.Int)); } return _cmdTempInsertShort; } } internal SqlCommand TempUpdateLong { get { if (_cmdTempUpdateLong == null) { _cmdTempUpdateLong = new SqlCommand("dbo.TempUpdateStateItemLong", _sqlConnection); _cmdTempUpdateLong.CommandType = CommandType.StoredProcedure; _cmdTempUpdateLong.CommandTimeout = s_commandTimeout; _cmdTempUpdateLong.Parameters.Add(new SqlParameter("@id", SqlDbType.NVarChar, ID_LENGTH)); _cmdTempUpdateLong.Parameters.Add(new SqlParameter("@itemLong", SqlDbType.Image, 8000)); _cmdTempUpdateLong.Parameters.Add(new SqlParameter("@timeout", SqlDbType.Int)); _cmdTempUpdateLong.Parameters.Add(new SqlParameter("@lockCookie", SqlDbType.Int)); } return _cmdTempUpdateLong; } } internal SqlCommand TempUpdateShort { get { /* Update */ if (_cmdTempUpdateShort == null) { _cmdTempUpdateShort = new SqlCommand("dbo.TempUpdateStateItemShort", _sqlConnection); _cmdTempUpdateShort.CommandType = CommandType.StoredProcedure; _cmdTempUpdateShort.CommandTimeout = s_commandTimeout; _cmdTempUpdateShort.Parameters.Add(new SqlParameter("@id", SqlDbType.NVarChar, ID_LENGTH)); _cmdTempUpdateShort.Parameters.Add(new SqlParameter("@itemShort", SqlDbType.VarBinary, ITEM_SHORT_LENGTH)); _cmdTempUpdateShort.Parameters.Add(new SqlParameter("@timeout", SqlDbType.Int)); _cmdTempUpdateShort.Parameters.Add(new SqlParameter("@lockCookie", SqlDbType.Int)); } return _cmdTempUpdateShort; } } internal SqlCommand TempUpdateShortNullLong { get { if (_cmdTempUpdateShortNullLong == null) { _cmdTempUpdateShortNullLong = new SqlCommand("dbo.TempUpdateStateItemShortNullLong", _sqlConnection); _cmdTempUpdateShortNullLong.CommandType = CommandType.StoredProcedure; _cmdTempUpdateShortNullLong.CommandTimeout = s_commandTimeout; _cmdTempUpdateShortNullLong.Parameters.Add(new SqlParameter("@id", SqlDbType.NVarChar, ID_LENGTH)); _cmdTempUpdateShortNullLong.Parameters.Add(new SqlParameter("@itemShort", SqlDbType.VarBinary, ITEM_SHORT_LENGTH)); _cmdTempUpdateShortNullLong.Parameters.Add(new SqlParameter("@timeout", SqlDbType.Int)); _cmdTempUpdateShortNullLong.Parameters.Add(new SqlParameter("@lockCookie", SqlDbType.Int)); } return _cmdTempUpdateShortNullLong; } } internal SqlCommand TempUpdateLongNullShort { get { if (_cmdTempUpdateLongNullShort == null) { _cmdTempUpdateLongNullShort = new SqlCommand("dbo.TempUpdateStateItemLongNullShort", _sqlConnection); _cmdTempUpdateLongNullShort.CommandType = CommandType.StoredProcedure; _cmdTempUpdateLongNullShort.CommandTimeout = s_commandTimeout; _cmdTempUpdateLongNullShort.Parameters.Add(new SqlParameter("@id", SqlDbType.NVarChar, ID_LENGTH)); _cmdTempUpdateLongNullShort.Parameters.Add(new SqlParameter("@itemLong", SqlDbType.Image, 8000)); _cmdTempUpdateLongNullShort.Parameters.Add(new SqlParameter("@timeout", SqlDbType.Int)); _cmdTempUpdateLongNullShort.Parameters.Add(new SqlParameter("@lockCookie", SqlDbType.Int)); } return _cmdTempUpdateLongNullShort; } } internal SqlCommand TempRemove { get { if (_cmdTempRemove == null) { /* Remove */ _cmdTempRemove = new SqlCommand("dbo.TempRemoveStateItem", _sqlConnection); _cmdTempRemove.CommandType = CommandType.StoredProcedure; _cmdTempRemove.CommandTimeout = s_commandTimeout; _cmdTempRemove.Parameters.Add(new SqlParameter("@id", SqlDbType.NVarChar, ID_LENGTH)); _cmdTempRemove.Parameters.Add(new SqlParameter("@lockCookie", SqlDbType.Int)); } return _cmdTempRemove; } } internal SqlCommand TempInsertUninitializedItem { get { if (_cmdTempInsertUninitializedItem == null) { _cmdTempInsertUninitializedItem = new SqlCommand("dbo.TempInsertUninitializedItem", _sqlConnection); _cmdTempInsertUninitializedItem.CommandType = CommandType.StoredProcedure; _cmdTempInsertUninitializedItem.CommandTimeout = s_commandTimeout; _cmdTempInsertUninitializedItem.Parameters.Add(new SqlParameter("@id", SqlDbType.NVarChar, ID_LENGTH)); _cmdTempInsertUninitializedItem.Parameters.Add(new SqlParameter("@itemShort", SqlDbType.VarBinary, ITEM_SHORT_LENGTH)); _cmdTempInsertUninitializedItem.Parameters.Add(new SqlParameter("@timeout", SqlDbType.Int)); } return _cmdTempInsertUninitializedItem; } } internal SqlCommand TempResetTimeout { get { if (_cmdTempResetTimeout == null) { /* ResetTimeout */ _cmdTempResetTimeout = new SqlCommand("dbo.TempResetTimeout", _sqlConnection); _cmdTempResetTimeout.CommandType = CommandType.StoredProcedure; _cmdTempResetTimeout.CommandTimeout = s_commandTimeout; _cmdTempResetTimeout.Parameters.Add(new SqlParameter("@id", SqlDbType.NVarChar, ID_LENGTH)); } return _cmdTempResetTimeout; } } public void Dispose() { Debug.Trace("ResourcePool", "Disposing SqlStateConnection"); if (_sqlConnection != null) { _sqlConnection.Close(); _sqlConnection = null; PerfCounters.DecrementCounter(AppPerfCounter.SESSION_SQL_SERVER_CONNECTIONS); } } internal SqlConnection Connection { get { return _sqlConnection; } } } } }
Link Menu
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- ReturnType.cs
- CultureData.cs
- SchemaInfo.cs
- OutputScopeManager.cs
- DoubleLink.cs
- MdiWindowListItemConverter.cs
- SiteMapSection.cs
- IdentitySection.cs
- SchemaImporter.cs
- ThreadExceptionEvent.cs
- TypeExtensionSerializer.cs
- KeyPullup.cs
- DiscoveryClientBindingElement.cs
- SoapIncludeAttribute.cs
- TextRunCache.cs
- Root.cs
- handlecollector.cs
- AlternateViewCollection.cs
- basenumberconverter.cs
- WindowsIPAddress.cs
- VectorAnimationUsingKeyFrames.cs
- DesignerEditorPartChrome.cs
- ListViewItem.cs
- InteropBitmapSource.cs
- Win32MouseDevice.cs
- TimeSpanConverter.cs
- XmlDictionaryReader.cs
- DataGridHeadersVisibilityToVisibilityConverter.cs
- XamlReaderConstants.cs
- MessagePropertyVariants.cs
- CounterCreationDataCollection.cs
- EditorAttribute.cs
- GetRecipientRequest.cs
- XPathNavigatorReader.cs
- ScrollProviderWrapper.cs
- ListViewGroupCollectionEditor.cs
- SerializerWriterEventHandlers.cs
- SoapIncludeAttribute.cs
- ContractAdapter.cs
- PasswordBoxAutomationPeer.cs
- ZipFileInfoCollection.cs
- GridViewColumnHeaderAutomationPeer.cs
- PrintingPermission.cs
- SslStreamSecurityBindingElement.cs
- Point4DConverter.cs
- CompilerState.cs
- PackagePart.cs
- SecurityToken.cs
- Italic.cs
- PolicyValidationException.cs
- TreeViewItem.cs
- OdbcEnvironmentHandle.cs
- ReflectionHelper.cs
- WebPartDisplayModeEventArgs.cs
- ArithmeticException.cs
- EntityException.cs
- Descriptor.cs
- SmiRecordBuffer.cs
- BehaviorDragDropEventArgs.cs
- MetadataCacheItem.cs
- WebRequestModulesSection.cs
- TdsParserStateObject.cs
- XamlClipboardData.cs
- MetaType.cs
- XmlSchemaChoice.cs
- PackageStore.cs
- OnOperation.cs
- XmlSerializer.cs
- WebPartsPersonalizationAuthorization.cs
- TableHeaderCell.cs
- SoapEnumAttribute.cs
- BaseCollection.cs
- ZipPackagePart.cs
- InstanceCreationEditor.cs
- DecoderFallbackWithFailureFlag.cs
- SkewTransform.cs
- _NegoState.cs
- OdbcCommand.cs
- AppDomainEvidenceFactory.cs
- NonParentingControl.cs
- SerialPinChanges.cs
- IssuerInformation.cs
- EntityDescriptor.cs
- MultiSelector.cs
- FontSizeConverter.cs
- PolicyImporterElement.cs
- XmlSchemaChoice.cs
- EventDescriptor.cs
- EncoderExceptionFallback.cs
- WindowsTab.cs
- FormsAuthenticationTicket.cs
- SemanticResolver.cs
- SqlDataSourceCache.cs
- TimersDescriptionAttribute.cs
- OdbcParameterCollection.cs
- DateTimeFormat.cs
- ClaimSet.cs
- PolyBezierSegmentFigureLogic.cs
- WorkflowElementDialogWindow.xaml.cs
- XmlDownloadManager.cs