File: State\sqlstateclientmanager.cs
Project: ndp\fx\src\xsp\system\Web\System.Web.csproj (System.Web)
//------------------------------------------------------------------------------
// <copyright file="SqlSessionStateStore.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
 
/*
 * 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_isClearPoolInProgress;
        static int          s_commandTimeout;
        static TimeSpan     s_retryInterval;
        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;
        static bool         s_configCompressionEnabled;
 
        // 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 SQL_CANNOT_OPEN_DATABASE_FOR_LOGIN = 4060;
        const int SQL_TIMEOUT_EXPIRED = -2;
        const int APP_SUFFIX_LENGTH = 8;
        const int FIRST_RETRY_SLEEP_TIME = 5000;
        const int RETRY_SLEEP_TIME = 1000;
 
        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;
            s_configCompressionEnabled = config.CompressionEnabled;
 
            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;
            s_retryInterval = config.SqlConnectionRetryInterval;
 
            s_isClearPoolInProgress = 0;
 
            // 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 <identity>:
        //  - 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, s_retryInterval);
            }
 
            return conn;
        }
 
        void DisposeOrReuseConnection(ref SqlStateConnection conn, bool usePooling) {
            try {
                if (conn == null) {
                    return;
                }
 
                if (usePooling) {
                    conn.ClearAllParameters();
                    _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),
                                e);
            }
            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
 
                using(reader = SqlExecuteReaderWithRetry(cmd, CommandBehavior.Default)) {
 
                    /* 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];
                            }
                        } 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);
 
                using(stream = new MemoryStream(buf)) {
                    item = SessionStateUtility.DeserializeStoreData(context, stream, s_configCompressionEnabled);
                    _rqOrigStreamLen = (int) stream.Position;
                }
                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);
                SqlCommand cmd = conn.TempReleaseExclusive;
                cmd.Parameters[0].Value = id + _partitionInfo.AppSuffix;
                cmd.Parameters[1].Value = lockCookie;
                SqlExecuteNonQueryWithRetry(cmd, false, null);
 
            }
            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, s_configCompressionEnabled);
                }
                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;
                }
                SqlExecuteNonQueryWithRetry(cmd, 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);
                SqlCommand cmd = conn.TempRemove;
                cmd.Parameters[0].Value = id + _partitionInfo.AppSuffix;
                cmd.Parameters[1].Value = lockCookie;
                SqlExecuteNonQueryWithRetry(cmd, false, null);
            }
            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);
                SqlCommand cmd = conn.TempResetTimeout;
                cmd.Parameters[0].Value = id + _partitionInfo.AppSuffix;
                SqlExecuteNonQueryWithRetry(cmd, false, null);
            }
            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, s_configCompressionEnabled);
 
                conn = GetConnection(id, ref usePooling);
 
                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;
                SqlExecuteNonQueryWithRetry(cmd, true, id);
            }
            finally {
                DisposeOrReuseConnection(ref conn, usePooling);
            }
        }
 
        static bool IsInsertPKException(SqlException ex, bool ignoreInsertPKException, string id) {
            // If the severity is greater than 20, we have a serious error.
            // The server usually closes the connection in these cases.
           if (ex != null &&
                ex.Number == SQL_ERROR_PRIMARY_KEY_VIOLATION &&
                ignoreInsertPKException) {
 
                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.
                return true;
           }
           return false;
        }
 
        static bool IsFatalSqlException(SqlException ex) {
            // We will retry sql operations for serious errors.
            // We consider fatal exceptions any error with severity >= 20.
            // In this case, the SQL server closes the connection.
            // 
 
 
 
            if(ex != null &&
                (ex.Class >= 20 ||
                 ex.Number == SQL_CANNOT_OPEN_DATABASE_FOR_LOGIN ||
                 ex.Number == SQL_TIMEOUT_EXPIRED)) {
                return true;
            }
            return false;
        }
 
        static void ClearFlagForClearPoolInProgress() {
           // clear s_isClearPoolInProgress if it was set
           Interlocked.CompareExchange(ref s_isClearPoolInProgress, 0, 1);
        }
 
        static bool CanRetry(SqlException ex, SqlConnection conn,
                                            ref bool isFirstAttempt, ref DateTime endRetryTime) {
            if (s_retryInterval.Seconds <= 0) {
                // no retry policy set
                return false;
            }
            if (!IsFatalSqlException(ex)) {
                if (!isFirstAttempt) {
                    ClearFlagForClearPoolInProgress();
                }
                return false;
            }
            if (isFirstAttempt) {
                // check if someone has called ClearPool for this connection string
                // s_isClearPoolInProgress can be:
                // 0 - no one called ClearPool;
                // 1 - ClearPool is in progress or has already been called
 
                // If no one called ClearPool (s_isClearPoolInProgress = 0), then
                // make s_isClearPoolInProgress 1 and call clear pool
                if (0 == Interlocked.CompareExchange(ref s_isClearPoolInProgress, 1, 0)) {
                    Debug.Trace("SqlSessionStateStore", "CanRetry: Call ClearPool to destroy the corrupted connections in the pool");
                    SqlConnection.ClearPool(conn);
                }
 
                // First time we sleep longer than for subsequent retries.
                Thread.Sleep(FIRST_RETRY_SLEEP_TIME);
                endRetryTime = DateTime.UtcNow.Add(s_retryInterval);
 
                isFirstAttempt = false;
                return true;
            }
            if (DateTime.UtcNow > endRetryTime) {
                // the specified retry interval is up, we can't retry anymore
                if (!isFirstAttempt) {
                    ClearFlagForClearPoolInProgress();
                }
                return false;
            }
            // sleep the specified time and allow retry
            Thread.Sleep(RETRY_SLEEP_TIME);
            return true;
        }
 
        static int SqlExecuteNonQueryWithRetry(SqlCommand cmd, bool ignoreInsertPKException, string id) {
            bool isFirstAttempt = true;
            DateTime endRetryTime = DateTime.UtcNow;
 
            while(true) {
                try {
                   if (cmd.Connection.State != ConnectionState.Open) {
                       // reopen the connection
                       // (gets closed if a previous operation throwed a SQL exception with severity >= 20)
                       cmd.Connection.Open();
                   }
                   int result = cmd.ExecuteNonQuery();
                   // the operation succeeded
                   // If we retried, it's possible ClearPool has been called.
                   // In this case, we clear the flag that shows ClearPool is in progress.
                   if (!isFirstAttempt) {
                       ClearFlagForClearPoolInProgress();
                   }
                   return result;
                }
                catch (SqlException e) {
                   // if specified, ignore primary key violations
                   if (IsInsertPKException(e, ignoreInsertPKException, id)) {
                       // ignoreInsertPKException = insert && newItem
                       return -1;
                   }
 
                   if (!CanRetry(e, cmd.Connection, ref isFirstAttempt, ref endRetryTime)) {
                       // just throw, because not all conditions to retry are satisfied
                       ThrowSqlConnectionException(cmd.Connection, e);
                   }
                }
                catch (Exception e) {
                   // just throw, we have a different Exception
                   ThrowSqlConnectionException(cmd.Connection, e);
                }
            }
        }
 
        static SqlDataReader SqlExecuteReaderWithRetry(SqlCommand cmd, CommandBehavior cmdBehavior) {
            bool isFirstAttempt = true;
            DateTime endRetryTime = DateTime.UtcNow;
 
            while(true) {
                try {
                    if (cmd.Connection.State != ConnectionState.Open) {
                       // reopen the connection
                       // (gets closed if a previous operation throwed a SQL exception with severity >= 20)
                        cmd.Connection.Open();
                    }
                    SqlDataReader reader = cmd.ExecuteReader(cmdBehavior);
                    // the operation succeeded
                    if (!isFirstAttempt) {
                        ClearFlagForClearPoolInProgress();
                    }
                    return reader;
                }
                catch (SqlException e) {
                    if (!CanRetry(e, cmd.Connection, ref isFirstAttempt, ref endRetryTime)) {
                        // just throw, default to previous behavior
                        ThrowSqlConnectionException(cmd.Connection, e);
                    }
                }
                catch (Exception e) {
                   // just throw, we have a different Exception
                   ThrowSqlConnectionException(cmd.Connection, 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;
 
                using(reader = SqlExecuteReaderWithRetry(cmd, 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;
                    }
                }
 
                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;
 
                SqlExecuteNonQueryWithRetry(cmd, false, null);
                try {
                    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;
 
                    // AppDomainAppId 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.AppDomainAppId;
 
                    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, TimeSpan retryInterval) {
                Debug.Trace("SessionStateConnectionIdentity", "Connecting under " + WindowsIdentity.GetCurrent().Name);
 
                _partitionInfo = sqlPartitionInfo;
                _sqlConnection = new SqlConnection(sqlPartitionInfo.SqlConnectionString);
 
                bool isFirstAttempt = true;
                DateTime endRetryTime = DateTime.UtcNow;
 
                while(true) {
                    try {
                        _sqlConnection.Open();
                        // the operation succeeded, exit the loop
                        if(!isFirstAttempt) {
                            ClearFlagForClearPoolInProgress();
                        }
                        break;
                    }
                    catch (SqlException e) {
                        if (e != null &&
                            (e.Number == SQL_LOGIN_FAILED ||
                             e.Number == SQL_LOGIN_FAILED_2 ||
                             e.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));
                            ClearConnectionAndThrow(outerException);
                        }
                        if (!CanRetry(e, _sqlConnection, ref isFirstAttempt, ref endRetryTime))
                        {
                            // just throw, the retry conditions are not satisfied
                            ClearConnectionAndThrow(e);
                        }
                    }
                    catch (Exception e) {
                        // just throw, we have a different Exception
                        ClearConnectionAndThrow(e);
                    }
                }
 
                try {
                    _partitionInfo.InitSqlInfo(_sqlConnection);
                    Debug.Assert(sqlPartitionInfo.SupportFlags != SupportFlags.Uninitialized);
 
                    PerfCounters.IncrementCounter(AppPerfCounter.SESSION_SQL_SERVER_CONNECTIONS);
                }
                catch {
                    Dispose();
                    throw;
                }
            }
 
            void ClearConnectionAndThrow(Exception e) {
                SqlConnection connection = _sqlConnection;
                _sqlConnection = null;
                ThrowSqlConnectionException(connection, e);
            }
 
             internal void ClearAllParameters() {
                 ClearAllParameters(_cmdTempGet);
                 ClearAllParameters(_cmdTempGetExclusive);
                 ClearAllParameters(_cmdTempReleaseExclusive);
                 ClearAllParameters(_cmdTempInsertShort);
                 ClearAllParameters(_cmdTempInsertLong);
                 ClearAllParameters(_cmdTempUpdateShort);
                 ClearAllParameters(_cmdTempUpdateShortNullLong);
                 ClearAllParameters(_cmdTempUpdateLong);
                 ClearAllParameters(_cmdTempUpdateLongNullShort);
                 ClearAllParameters(_cmdTempRemove);
                 ClearAllParameters(_cmdTempResetTimeout);
                 ClearAllParameters(_cmdTempInsertUninitializedItem);
             }
 
             internal void ClearAllParameters(SqlCommand cmd) {
                 if (cmd == null) {
                     return;
                 }
 
                 foreach (SqlParameter param in cmd.Parameters) {
                     param.Value = Convert.DBNull;
                 }
             }
 
            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; }
            }
        }
    }
}