Index: PgSessionStateStoreProvider.cs =================================================================== --- PgSessionStateStoreProvider.cs (revision 137) +++ PgSessionStateStoreProvider.cs (working copy) @@ -45,858 +45,871 @@ namespace NauckIT.PostgreSQLProvider { - public class PgSessionStateStoreProvider : SessionStateStoreProviderBase - { - private const string s_tableName = "Sessions"; - private System.Timers.Timer m_expiredSessionDeletionTimer; - private string m_connectionString = string.Empty; - private string m_applicationName = string.Empty; - private SessionStateSection m_config = null; - private bool m_enableExpireCallback = false; - private SessionStateItemExpireCallback m_expireCallback = null; + public class PgSessionStateStoreProvider : SessionStateStoreProviderBase + { + private const string s_tableName = "Sessions"; + private System.Timers.Timer m_expiredSessionDeletionTimer; + private string m_connectionString = string.Empty; + private string m_applicationName = string.Empty; + private SessionStateSection m_config = null; + private bool m_enableExpireCallback = false; + private SessionStateItemExpireCallback m_expireCallback = null; - /// - /// System.Configuration.Provider.ProviderBase.Initialize Method - /// - public override void Initialize(string name, NameValueCollection config) - { - // Initialize values from web.config. - if (config == null) - throw new ArgumentNullException("config", Properties.Resources.ErrArgumentNull); + /// + /// System.Configuration.Provider.ProviderBase.Initialize Method + /// + public override void Initialize(string name, NameValueCollection config) + { + // Initialize values from web.config. + if (config == null) + throw new ArgumentNullException("config", Properties.Resources.ErrArgumentNull); - if (string.IsNullOrEmpty(name)) - name = Properties.Resources.SessionStoreProviderDefaultName; + if (string.IsNullOrEmpty(name)) + name = Properties.Resources.SessionStoreProviderDefaultName; - if (string.IsNullOrEmpty(config["description"])) - { - config.Remove("description"); - config.Add("description", Properties.Resources.SessionStoreProviderDefaultDescription); - } + if (string.IsNullOrEmpty(config["description"])) + { + config.Remove("description"); + config.Add("description", Properties.Resources.SessionStoreProviderDefaultDescription); + } - // Initialize the abstract base class. - base.Initialize(name, config); + // Initialize the abstract base class. + base.Initialize(name, config); - m_applicationName = PgMembershipProvider.GetConfigValue(config["applicationName"], HostingEnvironment.ApplicationVirtualPath); + m_applicationName = PgMembershipProvider.GetConfigValue(config["applicationName"], HostingEnvironment.ApplicationVirtualPath); - // Get connection string. - m_connectionString = PgMembershipProvider.GetConnectionString(config["connectionStringName"]); + // Get connection string. + m_connectionString = PgMembershipProvider.GetConnectionString(config["connectionStringName"]); - // Get configuration element. - m_config = (SessionStateSection)WebConfigurationManager.OpenWebConfiguration(HostingEnvironment.ApplicationVirtualPath).GetSection("system.web/sessionState"); + // Get configuration element. + m_config = (SessionStateSection)WebConfigurationManager.OpenWebConfiguration(HostingEnvironment.ApplicationVirtualPath).GetSection("system.web/sessionState"); - // Should automatic session garbage collection be turned on? - bool enableExpiredSessionAutoDeletion = Convert.ToBoolean(PgMembershipProvider.GetConfigValue(config["enableExpiredSessionAutoDeletion"], "false"), CultureInfo.InvariantCulture); - - if (!enableExpiredSessionAutoDeletion) - return; + // Should automatic session garbage collection be turned on? + bool enableExpiredSessionAutoDeletion = Convert.ToBoolean(PgMembershipProvider.GetConfigValue(config["enableExpiredSessionAutoDeletion"], "false"), CultureInfo.InvariantCulture); - m_enableExpireCallback = Convert.ToBoolean(PgMembershipProvider.GetConfigValue(config["enableSessionExpireCallback"], "false"), CultureInfo.InvariantCulture); + if (!enableExpiredSessionAutoDeletion) + return; - // Load session garbage collection configuration and setup garbage collection interval timer - double expiredSessionAutoDeletionInterval = Convert.ToDouble(PgMembershipProvider.GetConfigValue(config["expiredSessionAutoDeletionInterval"], "1800000"), CultureInfo.InvariantCulture); //default: 30 minutes + m_enableExpireCallback = Convert.ToBoolean(PgMembershipProvider.GetConfigValue(config["enableSessionExpireCallback"], "false"), CultureInfo.InvariantCulture); - m_expiredSessionDeletionTimer = new System.Timers.Timer(expiredSessionAutoDeletionInterval); - m_expiredSessionDeletionTimer.Elapsed += new System.Timers.ElapsedEventHandler(ExpiredSessionDeletionTimer_Elapsed); - m_expiredSessionDeletionTimer.Enabled = true; - m_expiredSessionDeletionTimer.AutoReset = true; - } + // Load session garbage collection configuration and setup garbage collection interval timer + double expiredSessionAutoDeletionInterval = Convert.ToDouble(PgMembershipProvider.GetConfigValue(config["expiredSessionAutoDeletionInterval"], "1800000"), CultureInfo.InvariantCulture); //default: 30 minutes - /// - /// SessionStateStoreProviderBase members - /// - #region SessionStateStoreProviderBase members + m_expiredSessionDeletionTimer = new System.Timers.Timer(expiredSessionAutoDeletionInterval); + m_expiredSessionDeletionTimer.Elapsed += new System.Timers.ElapsedEventHandler(ExpiredSessionDeletionTimer_Elapsed); + m_expiredSessionDeletionTimer.Enabled = true; + m_expiredSessionDeletionTimer.AutoReset = true; + } - public override void Dispose() - { - if (m_expiredSessionDeletionTimer == null) - return; + /// + /// SessionStateStoreProviderBase members + /// + #region SessionStateStoreProviderBase members - // cleanup timer - m_expiredSessionDeletionTimer.Stop(); - m_expiredSessionDeletionTimer.Dispose(); - m_expiredSessionDeletionTimer = null; - } + public override void Dispose() + { + if (m_expiredSessionDeletionTimer == null) + return; - /// - /// SessionStateProviderBase.InitializeRequest - /// - public override void InitializeRequest(HttpContext context) - { - } + // cleanup timer + m_expiredSessionDeletionTimer.Stop(); + m_expiredSessionDeletionTimer.Dispose(); + m_expiredSessionDeletionTimer = null; + } - /// - /// SessionStateProviderBase.EndRequest - /// - public override void EndRequest(HttpContext context) - { - } + /// + /// SessionStateProviderBase.InitializeRequest + /// + public override void InitializeRequest(HttpContext context) + { + } - /// - /// SessionStateProviderBase.CreateNewStoreData - /// - public override SessionStateStoreData CreateNewStoreData(HttpContext context, int timeout) - { - return new SessionStateStoreData(new SessionStateItemCollection(), SessionStateUtility.GetSessionStaticObjects(context), timeout); - } + /// + /// SessionStateProviderBase.EndRequest + /// + public override void EndRequest(HttpContext context) + { + } - /// - /// SessionStateProviderBase.CreateUninitializedItem - /// - public override void CreateUninitializedItem(HttpContext context, string id, int timeout) - { - using (NpgsqlConnection dbConn = new NpgsqlConnection(m_connectionString)) - { - using (NpgsqlCommand dbCommand = dbConn.CreateCommand()) - { - dbCommand.CommandText = string.Format(CultureInfo.InvariantCulture, "INSERT INTO \"{0}\" (\"SessionId\", \"ApplicationName\", \"Created\", \"Expires\", \"Timeout\", \"Locked\", \"LockId\", \"LockDate\", \"Data\", \"Flags\") Values (@SessionId, @ApplicationName, @Created, @Expires, @Timeout, @Locked, @LockId, @LockDate, @Data, @Flags)", s_tableName); + /// + /// SessionStateProviderBase.CreateNewStoreData + /// + public override SessionStateStoreData CreateNewStoreData(HttpContext context, int timeout) + { + return new SessionStateStoreData(new SessionStateItemCollection(), SessionStateUtility.GetSessionStaticObjects(context), timeout); + } - dbCommand.Parameters.Add("@SessionId", NpgsqlDbType.Varchar, 80).Value = id; - dbCommand.Parameters.Add("@ApplicationName", NpgsqlDbType.Varchar, 255).Value = m_applicationName; - dbCommand.Parameters.Add("@Created", NpgsqlDbType.TimestampTZ).Value = DateTime.Now; - dbCommand.Parameters.Add("@Expires", NpgsqlDbType.TimestampTZ).Value = DateTime.Now.AddMinutes((Double)timeout); - dbCommand.Parameters.Add("@Timeout", NpgsqlDbType.Integer).Value = timeout; - dbCommand.Parameters.Add("@Locked", NpgsqlDbType.Boolean).Value = false; - dbCommand.Parameters.Add("@LockId", NpgsqlDbType.Integer).Value = 0; - dbCommand.Parameters.Add("@LockDate", NpgsqlDbType.TimestampTZ).Value = DateTime.Now; - dbCommand.Parameters.Add("@Data", NpgsqlDbType.Text).Value = string.Empty; - dbCommand.Parameters.Add("@Flags", NpgsqlDbType.Integer).Value = 1; + /// + /// SessionStateProviderBase.CreateUninitializedItem + /// + public override void CreateUninitializedItem(HttpContext context, string id, int timeout) + { + using (NpgsqlConnection dbConn = new NpgsqlConnection(m_connectionString)) + { + using (NpgsqlCommand dbCommand = dbConn.CreateCommand()) + { + NpgsqlTransaction dbTrans = null; - NpgsqlTransaction dbTrans = null; + try + { + dbConn.Open(); + dbTrans = dbConn.BeginTransaction(); - try - { - dbConn.Open(); - dbCommand.Prepare(); + /* + * PATCH (cti, fixes bug #16): To prevent running into duplicate key constraint in postgres, delete Session first. + */ + dbCommand.CommandText = string.Format(CultureInfo.InvariantCulture, "DELETE FROM \"{0}\" WHERE \"SessionId\" = @SessionId;", s_tableName); - dbTrans = dbConn.BeginTransaction(); + dbCommand.Parameters.Add("@SessionId", NpgsqlDbType.Varchar, 80).Value = id; - dbCommand.ExecuteNonQuery(); + dbCommand.Prepare(); - // Attempt to commit the transaction - dbTrans.Commit(); - } - catch (Exception e) - { - Trace.WriteLine(e.ToString()); + dbCommand.ExecuteNonQuery(); + /* PATCH END */ - if (dbTrans != null) - { - try - { - // Attempt to roll back the transaction - Trace.WriteLine(Properties.Resources.LogRollbackAttempt); - dbTrans.Rollback(); - } - catch (NpgsqlException re) - { - // Rollback failed - Trace.WriteLine(Properties.Resources.ErrRollbackFailed); - Trace.WriteLine(re.ToString()); - } - } + dbCommand.CommandText = string.Format(CultureInfo.InvariantCulture, "INSERT INTO \"{0}\" (\"SessionId\", \"ApplicationName\", \"Created\", \"Expires\", \"Timeout\", \"Locked\", \"LockId\", \"LockDate\", \"Data\", \"Flags\") Values (@SessionId, @ApplicationName, @Created, @Expires, @Timeout, @Locked, @LockId, @LockDate, @Data, @Flags)", s_tableName); - throw new ProviderException(Properties.Resources.ErrOperationAborted); - } - finally - { - if (dbTrans != null) - dbTrans.Dispose(); + dbCommand.Parameters.Clear(); + dbCommand.Parameters.Add("@SessionId", NpgsqlDbType.Varchar, 80).Value = id; + dbCommand.Parameters.Add("@ApplicationName", NpgsqlDbType.Varchar, 255).Value = m_applicationName; + dbCommand.Parameters.Add("@Created", NpgsqlDbType.TimestampTZ).Value = DateTime.Now; + dbCommand.Parameters.Add("@Expires", NpgsqlDbType.TimestampTZ).Value = DateTime.Now.AddMinutes((Double)timeout); + dbCommand.Parameters.Add("@Timeout", NpgsqlDbType.Integer).Value = timeout; + dbCommand.Parameters.Add("@Locked", NpgsqlDbType.Boolean).Value = false; + dbCommand.Parameters.Add("@LockId", NpgsqlDbType.Integer).Value = 0; + dbCommand.Parameters.Add("@LockDate", NpgsqlDbType.TimestampTZ).Value = DateTime.Now; + dbCommand.Parameters.Add("@Data", NpgsqlDbType.Text).Value = string.Empty; + dbCommand.Parameters.Add("@Flags", NpgsqlDbType.Integer).Value = 1; - if (dbConn != null) - dbConn.Close(); - } - } - } - } + dbCommand.Prepare(); - /// - /// SessionStateProviderBase.GetItem - /// - public override SessionStateStoreData GetItem(HttpContext context, string id, out bool locked, out TimeSpan lockAge, out object lockId, out SessionStateActions actions) - { - return GetSessionStoreItem(false, context, id, out locked, out lockAge, out lockId, out actions); - } + dbCommand.ExecuteNonQuery(); - /// - /// SessionStateProviderBase.GetItemExclusive - /// - public override SessionStateStoreData GetItemExclusive(HttpContext context, string id, out bool locked, out TimeSpan lockAge, out object lockId, out SessionStateActions actions) - { - return GetSessionStoreItem(true, context, id, out locked, out lockAge, out lockId, out actions); - } + // Attempt to commit the transaction + dbTrans.Commit(); + } + catch (Exception e) + { + Trace.WriteLine(e.ToString()); - /// - /// SessionStateProviderBase.ReleaseItemExclusive - /// - public override void ReleaseItemExclusive(HttpContext context, string id, object lockId) - { - using (NpgsqlConnection dbConn = new NpgsqlConnection(m_connectionString)) - { - using (NpgsqlCommand dbCommand = dbConn.CreateCommand()) - { - dbCommand.CommandText = string.Format(CultureInfo.InvariantCulture, "UPDATE \"{0}\" SET \"Expires\" = @Expires, \"Locked\" = @Locked WHERE \"SessionId\" = @SessionId AND \"ApplicationName\" = @ApplicationName AND \"LockId\" = @LockId", s_tableName); + if (dbTrans != null) + { + try + { + // Attempt to roll back the transaction + Trace.WriteLine(Properties.Resources.LogRollbackAttempt); + dbTrans.Rollback(); + } + catch (NpgsqlException re) + { + // Rollback failed + Trace.WriteLine(Properties.Resources.ErrRollbackFailed); + Trace.WriteLine(re.ToString()); + } + } - dbCommand.Parameters.Add("@Expires", NpgsqlDbType.TimestampTZ).Value = DateTime.Now.Add(m_config.Timeout); - dbCommand.Parameters.Add("@Locked", NpgsqlDbType.Boolean).Value = false; - dbCommand.Parameters.Add("@SessionId", NpgsqlDbType.Varchar, 80).Value = id; - dbCommand.Parameters.Add("@ApplicationName", NpgsqlDbType.Varchar, 255).Value = m_applicationName; - dbCommand.Parameters.Add("@LockId", NpgsqlDbType.Integer).Value = lockId; - - NpgsqlTransaction dbTrans = null; + throw new ProviderException(Properties.Resources.ErrOperationAborted); + } + finally + { + if (dbTrans != null) + dbTrans.Dispose(); - try - { - dbConn.Open(); - dbCommand.Prepare(); + if (dbConn != null) + dbConn.Close(); + } + } + } + } - dbTrans = dbConn.BeginTransaction(); + /// + /// SessionStateProviderBase.GetItem + /// + public override SessionStateStoreData GetItem(HttpContext context, string id, out bool locked, out TimeSpan lockAge, out object lockId, out SessionStateActions actions) + { + return GetSessionStoreItem(false, context, id, out locked, out lockAge, out lockId, out actions); + } - dbCommand.ExecuteNonQuery(); + /// + /// SessionStateProviderBase.GetItemExclusive + /// + public override SessionStateStoreData GetItemExclusive(HttpContext context, string id, out bool locked, out TimeSpan lockAge, out object lockId, out SessionStateActions actions) + { + return GetSessionStoreItem(true, context, id, out locked, out lockAge, out lockId, out actions); + } - // Attempt to commit the transaction - dbTrans.Commit(); - } - catch (NpgsqlException e) - { - Trace.WriteLine(e.ToString()); + /// + /// SessionStateProviderBase.ReleaseItemExclusive + /// + public override void ReleaseItemExclusive(HttpContext context, string id, object lockId) + { + using (NpgsqlConnection dbConn = new NpgsqlConnection(m_connectionString)) + { + using (NpgsqlCommand dbCommand = dbConn.CreateCommand()) + { + dbCommand.CommandText = string.Format(CultureInfo.InvariantCulture, "UPDATE \"{0}\" SET \"Expires\" = @Expires, \"Locked\" = @Locked WHERE \"SessionId\" = @SessionId AND \"ApplicationName\" = @ApplicationName AND \"LockId\" = @LockId", s_tableName); - if (dbTrans != null) - { - try - { - // Attempt to roll back the transaction - Trace.WriteLine(Properties.Resources.LogRollbackAttempt); - dbTrans.Rollback(); - } - catch (NpgsqlException re) - { - // Rollback failed - Trace.WriteLine(Properties.Resources.ErrRollbackFailed); - Trace.WriteLine(re.ToString()); - } - } + dbCommand.Parameters.Add("@Expires", NpgsqlDbType.TimestampTZ).Value = DateTime.Now.Add(m_config.Timeout); + dbCommand.Parameters.Add("@Locked", NpgsqlDbType.Boolean).Value = false; + dbCommand.Parameters.Add("@SessionId", NpgsqlDbType.Varchar, 80).Value = id; + dbCommand.Parameters.Add("@ApplicationName", NpgsqlDbType.Varchar, 255).Value = m_applicationName; + dbCommand.Parameters.Add("@LockId", NpgsqlDbType.Integer).Value = lockId; - throw new ProviderException(Properties.Resources.ErrOperationAborted); - } - finally - { - if (dbTrans != null) - dbTrans.Dispose(); + NpgsqlTransaction dbTrans = null; - if (dbConn != null) - dbConn.Close(); - } - } - } - } + try + { + dbConn.Open(); + dbCommand.Prepare(); - /// - /// SessionStateProviderBase.RemoveItem - /// - public override void RemoveItem(HttpContext context, string id, object lockId, SessionStateStoreData item) - { - using (NpgsqlConnection dbConn = new NpgsqlConnection(m_connectionString)) - { - using (NpgsqlCommand dbCommand = dbConn.CreateCommand()) - { - dbCommand.CommandText = string.Format(CultureInfo.InvariantCulture, "DELETE FROM \"{0}\" WHERE \"SessionId\" = @SessionId AND \"ApplicationName\" = @ApplicationName AND \"LockId\" = @LockId", s_tableName); + dbTrans = dbConn.BeginTransaction(); - dbCommand.Parameters.Add("@SessionId", NpgsqlDbType.Varchar, 80).Value = id; - dbCommand.Parameters.Add("@ApplicationName", NpgsqlDbType.Varchar, 255).Value = m_applicationName; - dbCommand.Parameters.Add("@LockId", NpgsqlDbType.Integer).Value = lockId; + dbCommand.ExecuteNonQuery(); - NpgsqlTransaction dbTrans = null; + // Attempt to commit the transaction + dbTrans.Commit(); + } + catch (NpgsqlException e) + { + Trace.WriteLine(e.ToString()); - try - { - dbConn.Open(); - dbCommand.Prepare(); + if (dbTrans != null) + { + try + { + // Attempt to roll back the transaction + Trace.WriteLine(Properties.Resources.LogRollbackAttempt); + dbTrans.Rollback(); + } + catch (NpgsqlException re) + { + // Rollback failed + Trace.WriteLine(Properties.Resources.ErrRollbackFailed); + Trace.WriteLine(re.ToString()); + } + } - dbTrans = dbConn.BeginTransaction(); + throw new ProviderException(Properties.Resources.ErrOperationAborted); + } + finally + { + if (dbTrans != null) + dbTrans.Dispose(); - dbCommand.ExecuteNonQuery(); + if (dbConn != null) + dbConn.Close(); + } + } + } + } - // Attempt to commit the transaction - dbTrans.Commit(); - } - catch (Exception e) - { - Trace.WriteLine(e.ToString()); + /// + /// SessionStateProviderBase.RemoveItem + /// + public override void RemoveItem(HttpContext context, string id, object lockId, SessionStateStoreData item) + { + using (NpgsqlConnection dbConn = new NpgsqlConnection(m_connectionString)) + { + using (NpgsqlCommand dbCommand = dbConn.CreateCommand()) + { + dbCommand.CommandText = string.Format(CultureInfo.InvariantCulture, "DELETE FROM \"{0}\" WHERE \"SessionId\" = @SessionId AND \"ApplicationName\" = @ApplicationName AND \"LockId\" = @LockId", s_tableName); - if (dbTrans != null) - { - try - { - // Attempt to roll back the transaction - Trace.WriteLine(Properties.Resources.LogRollbackAttempt); - dbTrans.Rollback(); - } - catch (Exception re) - { - // Rollback failed - Trace.WriteLine(Properties.Resources.ErrRollbackFailed); - Trace.WriteLine(re.ToString()); - } - } + dbCommand.Parameters.Add("@SessionId", NpgsqlDbType.Varchar, 80).Value = id; + dbCommand.Parameters.Add("@ApplicationName", NpgsqlDbType.Varchar, 255).Value = m_applicationName; + dbCommand.Parameters.Add("@LockId", NpgsqlDbType.Integer).Value = lockId; - throw new ProviderException(Properties.Resources.ErrOperationAborted); - } - finally - { - if (dbTrans != null) - dbTrans.Dispose(); + NpgsqlTransaction dbTrans = null; - if (dbConn != null) - dbConn.Close(); - } - } - } - } + try + { + dbConn.Open(); + dbCommand.Prepare(); - /// - /// SessionStateProviderBase.ResetItemTimeout - /// - public override void ResetItemTimeout(HttpContext context, string id) - { - using (NpgsqlConnection dbConn = new NpgsqlConnection(m_connectionString)) - { - using (NpgsqlCommand dbCommand = dbConn.CreateCommand()) - { - dbCommand.CommandText = string.Format(CultureInfo.InvariantCulture, "UPDATE \"{0}\" SET \"Expires\" = @Expires WHERE \"SessionId\" = @SessionId AND \"ApplicationName\" = @ApplicationName", s_tableName); + dbTrans = dbConn.BeginTransaction(); - dbCommand.Parameters.Add("@Expires", NpgsqlDbType.TimestampTZ).Value = DateTime.Now.Add(m_config.Timeout); - dbCommand.Parameters.Add("@SessionId", NpgsqlDbType.Varchar, 80).Value = id; - dbCommand.Parameters.Add("@ApplicationName", NpgsqlDbType.Varchar, 255).Value = m_applicationName; + dbCommand.ExecuteNonQuery(); - NpgsqlTransaction dbTrans = null; + // Attempt to commit the transaction + dbTrans.Commit(); + } + catch (Exception e) + { + Trace.WriteLine(e.ToString()); - try - { - dbConn.Open(); - dbCommand.Prepare(); + if (dbTrans != null) + { + try + { + // Attempt to roll back the transaction + Trace.WriteLine(Properties.Resources.LogRollbackAttempt); + dbTrans.Rollback(); + } + catch (Exception re) + { + // Rollback failed + Trace.WriteLine(Properties.Resources.ErrRollbackFailed); + Trace.WriteLine(re.ToString()); + } + } - dbTrans = dbConn.BeginTransaction(); + throw new ProviderException(Properties.Resources.ErrOperationAborted); + } + finally + { + if (dbTrans != null) + dbTrans.Dispose(); - dbCommand.ExecuteNonQuery(); + if (dbConn != null) + dbConn.Close(); + } + } + } + } - // Attempt to commit the transaction - dbTrans.Commit(); - } - catch (Exception e) - { - Trace.WriteLine(e.ToString()); + /// + /// SessionStateProviderBase.ResetItemTimeout + /// + public override void ResetItemTimeout(HttpContext context, string id) + { + using (NpgsqlConnection dbConn = new NpgsqlConnection(m_connectionString)) + { + using (NpgsqlCommand dbCommand = dbConn.CreateCommand()) + { + dbCommand.CommandText = string.Format(CultureInfo.InvariantCulture, "UPDATE \"{0}\" SET \"Expires\" = @Expires WHERE \"SessionId\" = @SessionId AND \"ApplicationName\" = @ApplicationName", s_tableName); - if (dbTrans != null) - { - try - { - // Attempt to roll back the transaction - Trace.WriteLine(Properties.Resources.LogRollbackAttempt); - dbTrans.Rollback(); - } - catch (Exception re) - { - // Rollback failed - Trace.WriteLine(Properties.Resources.ErrRollbackFailed); - Trace.WriteLine(re.ToString()); - } - } + dbCommand.Parameters.Add("@Expires", NpgsqlDbType.TimestampTZ).Value = DateTime.Now.Add(m_config.Timeout); + dbCommand.Parameters.Add("@SessionId", NpgsqlDbType.Varchar, 80).Value = id; + dbCommand.Parameters.Add("@ApplicationName", NpgsqlDbType.Varchar, 255).Value = m_applicationName; - throw new ProviderException(Properties.Resources.ErrOperationAborted); - } - finally - { - if (dbTrans != null) - dbTrans.Dispose(); + NpgsqlTransaction dbTrans = null; - if (dbConn != null) - dbConn.Close(); - } - } - } - } + try + { + dbConn.Open(); + dbCommand.Prepare(); - /// - /// SessionStateProviderBase.SetAndReleaseItemExclusive - /// - public override void SetAndReleaseItemExclusive(HttpContext context, string id, SessionStateStoreData item, object lockId, bool newItem) - { - // Serialize the SessionStateItemCollection as a string - string serializedItems = Serialize((SessionStateItemCollection)item.Items); + dbTrans = dbConn.BeginTransaction(); - using (NpgsqlConnection dbConn = new NpgsqlConnection(m_connectionString)) - { - using (NpgsqlCommand dbCommand = dbConn.CreateCommand(), - delCommand = dbConn.CreateCommand()) - { - if (newItem) - { - // Delete existing expired session if exist - delCommand.CommandText = string.Format(CultureInfo.InvariantCulture, "DELETE FROM \"{0}\" WHERE \"SessionId\" = @SessionId AND \"ApplicationName\" = @ApplicationName", s_tableName); + dbCommand.ExecuteNonQuery(); - delCommand.Parameters.Add("@SessionId", NpgsqlDbType.Varchar, 80).Value = id; - delCommand.Parameters.Add("@ApplicationName", NpgsqlDbType.Varchar, 255).Value = m_applicationName; + // Attempt to commit the transaction + dbTrans.Commit(); + } + catch (Exception e) + { + Trace.WriteLine(e.ToString()); - // Insert new session data - dbCommand.CommandText = string.Format(CultureInfo.InvariantCulture, "INSERT INTO \"{0}\" (\"SessionId\", \"ApplicationName\", \"Created\", \"Expires\", \"Timeout\", \"Locked\", \"LockId\", \"LockDate\", \"Data\", \"Flags\") Values (@SessionId, @ApplicationName, @Created, @Expires, @Timeout, @Locked, @LockId, @LockDate, @Data, @Flags)", s_tableName); + if (dbTrans != null) + { + try + { + // Attempt to roll back the transaction + Trace.WriteLine(Properties.Resources.LogRollbackAttempt); + dbTrans.Rollback(); + } + catch (Exception re) + { + // Rollback failed + Trace.WriteLine(Properties.Resources.ErrRollbackFailed); + Trace.WriteLine(re.ToString()); + } + } - dbCommand.Parameters.Add("@SessionId", NpgsqlDbType.Varchar, 80).Value = id; - dbCommand.Parameters.Add("@ApplicationName", NpgsqlDbType.Varchar, 255).Value = m_applicationName; - dbCommand.Parameters.Add("@Created", NpgsqlDbType.TimestampTZ).Value = DateTime.Now; - dbCommand.Parameters.Add("@Expires", NpgsqlDbType.TimestampTZ).Value = DateTime.Now.AddMinutes((Double)item.Timeout); - dbCommand.Parameters.Add("@Timeout", NpgsqlDbType.Integer).Value = item.Timeout; - dbCommand.Parameters.Add("@Locked", NpgsqlDbType.Boolean).Value = false; - dbCommand.Parameters.Add("@LockId", NpgsqlDbType.Integer).Value = 0; - dbCommand.Parameters.Add("@LockDate", NpgsqlDbType.TimestampTZ).Value = DateTime.Now; - dbCommand.Parameters.Add("@Data", NpgsqlDbType.Text).Value = serializedItems; - dbCommand.Parameters.Add("@Flags", NpgsqlDbType.Integer).Value = 0; - } - else - { - // Update existing session - dbCommand.CommandText = string.Format(CultureInfo.InvariantCulture, "UPDATE \"{0}\" SET \"Expires\" = @Expires, \"Locked\" = @Locked, \"Data\" = @Data WHERE \"SessionId\" = @SessionId AND \"ApplicationName\" = @ApplicationName AND \"LockId\" = @LockId", s_tableName); + throw new ProviderException(Properties.Resources.ErrOperationAborted); + } + finally + { + if (dbTrans != null) + dbTrans.Dispose(); - dbCommand.Parameters.Add("@Expires", NpgsqlDbType.TimestampTZ).Value = DateTime.Now.AddMinutes((Double)item.Timeout); - dbCommand.Parameters.Add("@Locked", NpgsqlDbType.Boolean).Value = false; - dbCommand.Parameters.Add("@Data", NpgsqlDbType.Text).Value = serializedItems; - dbCommand.Parameters.Add("@SessionId", NpgsqlDbType.Varchar, 80).Value = id; - dbCommand.Parameters.Add("@ApplicationName", NpgsqlDbType.Varchar, 255).Value = m_applicationName; - dbCommand.Parameters.Add("@LockId", NpgsqlDbType.Integer).Value = lockId; - } + if (dbConn != null) + dbConn.Close(); + } + } + } + } - NpgsqlTransaction dbTrans = null; + /// + /// SessionStateProviderBase.SetAndReleaseItemExclusive + /// + public override void SetAndReleaseItemExclusive(HttpContext context, string id, SessionStateStoreData item, object lockId, bool newItem) + { + // Serialize the SessionStateItemCollection as a string + string serializedItems = Serialize((SessionStateItemCollection)item.Items); - try - { - dbConn.Open(); - dbTrans = dbConn.BeginTransaction(); + using (NpgsqlConnection dbConn = new NpgsqlConnection(m_connectionString)) + { + using (NpgsqlCommand dbCommand = dbConn.CreateCommand(), + delCommand = dbConn.CreateCommand()) + { + if (newItem) + { + // Delete existing expired session if exist + delCommand.CommandText = string.Format(CultureInfo.InvariantCulture, "DELETE FROM \"{0}\" WHERE \"SessionId\" = @SessionId AND \"ApplicationName\" = @ApplicationName", s_tableName); - if (newItem) - { - delCommand.Prepare(); - delCommand.ExecuteNonQuery(); - } + delCommand.Parameters.Add("@SessionId", NpgsqlDbType.Varchar, 80).Value = id; + delCommand.Parameters.Add("@ApplicationName", NpgsqlDbType.Varchar, 255).Value = m_applicationName; - dbCommand.Prepare(); - dbCommand.ExecuteNonQuery(); + // Insert new session data + dbCommand.CommandText = string.Format(CultureInfo.InvariantCulture, "INSERT INTO \"{0}\" (\"SessionId\", \"ApplicationName\", \"Created\", \"Expires\", \"Timeout\", \"Locked\", \"LockId\", \"LockDate\", \"Data\", \"Flags\") Values (@SessionId, @ApplicationName, @Created, @Expires, @Timeout, @Locked, @LockId, @LockDate, @Data, @Flags)", s_tableName); - // Attempt to commit the transaction - dbTrans.Commit(); - } - catch (Exception e) - { - Trace.WriteLine(e.ToString()); + dbCommand.Parameters.Add("@SessionId", NpgsqlDbType.Varchar, 80).Value = id; + dbCommand.Parameters.Add("@ApplicationName", NpgsqlDbType.Varchar, 255).Value = m_applicationName; + dbCommand.Parameters.Add("@Created", NpgsqlDbType.TimestampTZ).Value = DateTime.Now; + dbCommand.Parameters.Add("@Expires", NpgsqlDbType.TimestampTZ).Value = DateTime.Now.AddMinutes((Double)item.Timeout); + dbCommand.Parameters.Add("@Timeout", NpgsqlDbType.Integer).Value = item.Timeout; + dbCommand.Parameters.Add("@Locked", NpgsqlDbType.Boolean).Value = false; + dbCommand.Parameters.Add("@LockId", NpgsqlDbType.Integer).Value = 0; + dbCommand.Parameters.Add("@LockDate", NpgsqlDbType.TimestampTZ).Value = DateTime.Now; + dbCommand.Parameters.Add("@Data", NpgsqlDbType.Text).Value = serializedItems; + dbCommand.Parameters.Add("@Flags", NpgsqlDbType.Integer).Value = 0; + } + else + { + // Update existing session + dbCommand.CommandText = string.Format(CultureInfo.InvariantCulture, "UPDATE \"{0}\" SET \"Expires\" = @Expires, \"Locked\" = @Locked, \"Data\" = @Data WHERE \"SessionId\" = @SessionId AND \"ApplicationName\" = @ApplicationName AND \"LockId\" = @LockId", s_tableName); - if (dbTrans != null) - { - try - { - // Attempt to roll back the transaction - Trace.WriteLine(Properties.Resources.LogRollbackAttempt); - dbTrans.Rollback(); - } - catch (Exception re) - { - // Rollback failed - Trace.WriteLine(Properties.Resources.ErrRollbackFailed); - Trace.WriteLine(re.ToString()); - } - } + dbCommand.Parameters.Add("@Expires", NpgsqlDbType.TimestampTZ).Value = DateTime.Now.AddMinutes((Double)item.Timeout); + dbCommand.Parameters.Add("@Locked", NpgsqlDbType.Boolean).Value = false; + dbCommand.Parameters.Add("@Data", NpgsqlDbType.Text).Value = serializedItems; + dbCommand.Parameters.Add("@SessionId", NpgsqlDbType.Varchar, 80).Value = id; + dbCommand.Parameters.Add("@ApplicationName", NpgsqlDbType.Varchar, 255).Value = m_applicationName; + dbCommand.Parameters.Add("@LockId", NpgsqlDbType.Integer).Value = lockId; + } - throw new ProviderException(Properties.Resources.ErrOperationAborted); - } - finally - { - if (dbTrans != null) - dbTrans.Dispose(); + NpgsqlTransaction dbTrans = null; - if (dbConn != null) - dbConn.Close(); - } - } - } - } + try + { + dbConn.Open(); + dbTrans = dbConn.BeginTransaction(); - /// - /// SessionStateProviderBase.SetItemExpireCallback - /// - public override bool SetItemExpireCallback(SessionStateItemExpireCallback expireCallback) - { - // Accept and store callback if session expire callback is enabled. If not, return false in order to inform SessionStateModule - // the session expire callback is not supported. - if (!m_enableExpireCallback) - return false; + if (newItem) + { + delCommand.Prepare(); + delCommand.ExecuteNonQuery(); + } - m_expireCallback = expireCallback; - return true; - } + dbCommand.Prepare(); + dbCommand.ExecuteNonQuery(); - #endregion + // Attempt to commit the transaction + dbTrans.Commit(); + } + catch (Exception e) + { + Trace.WriteLine(e.ToString()); - #region private methods + if (dbTrans != null) + { + try + { + // Attempt to roll back the transaction + Trace.WriteLine(Properties.Resources.LogRollbackAttempt); + dbTrans.Rollback(); + } + catch (Exception re) + { + // Rollback failed + Trace.WriteLine(Properties.Resources.ErrRollbackFailed); + Trace.WriteLine(re.ToString()); + } + } - /// - /// Retrieves the session data from the data source. - /// - /// If true GetSessionStoreItem locks the record and sets a new LockId and LockDate. - private SessionStateStoreData GetSessionStoreItem(bool lockRecord, HttpContext context, string id, out bool locked, out TimeSpan lockAge, out object lockId, out SessionStateActions actionFlags) - { - SessionStateStoreData result = null; - lockAge = TimeSpan.Zero; - lockId = null; - locked = false; - actionFlags = 0; - DateTime expires = DateTime.MinValue; - int timeout = 0; - string serializedItems = null; + throw new ProviderException(Properties.Resources.ErrOperationAborted); + } + finally + { + if (dbTrans != null) + dbTrans.Dispose(); - using (NpgsqlConnection dbConn = new NpgsqlConnection(m_connectionString)) - { - NpgsqlTransaction dbTrans = null; - try - { - dbConn.Open(); - dbTrans = dbConn.BeginTransaction(); + if (dbConn != null) + dbConn.Close(); + } + } + } + } - // Retrieve the current session item information and lock row - using (NpgsqlCommand dbCommand = dbConn.CreateCommand()) - { - dbCommand.CommandText = string.Format(CultureInfo.InvariantCulture, "SELECT \"Expires\", \"Timeout\", \"Locked\", \"LockId\", \"LockDate\", \"Data\", \"Flags\" FROM \"{0}\" WHERE \"SessionId\" = @SessionId AND \"ApplicationName\" = @ApplicationName FOR UPDATE", s_tableName); + /// + /// SessionStateProviderBase.SetItemExpireCallback + /// + public override bool SetItemExpireCallback(SessionStateItemExpireCallback expireCallback) + { + // Accept and store callback if session expire callback is enabled. If not, return false in order to inform SessionStateModule + // the session expire callback is not supported. + if (!m_enableExpireCallback) + return false; - dbCommand.Parameters.Add("@SessionId", NpgsqlDbType.Varchar, 80).Value = id; - dbCommand.Parameters.Add("@ApplicationName", NpgsqlDbType.Varchar, 255).Value = m_applicationName; + m_expireCallback = expireCallback; + return true; + } - using (NpgsqlDataReader reader = dbCommand.ExecuteReader(System.Data.CommandBehavior.SingleRow)) - { - while (reader.Read()) - { - expires = reader.GetDateTime(0); - timeout = reader.GetInt32(1); - locked = reader.GetBoolean(2); - lockId = reader.GetInt32(3); - lockAge = DateTime.Now.Subtract(reader.GetDateTime(4)); + #endregion - if (!reader.IsDBNull(5)) - serializedItems = reader.GetString(5); + #region private methods - actionFlags = (SessionStateActions)reader.GetInt32(6); - } - reader.Close(); - } - } + /// + /// Retrieves the session data from the data source. + /// + /// If true GetSessionStoreItem locks the record and sets a new LockId and LockDate. + private SessionStateStoreData GetSessionStoreItem(bool lockRecord, HttpContext context, string id, out bool locked, out TimeSpan lockAge, out object lockId, out SessionStateActions actionFlags) + { + SessionStateStoreData result = null; + lockAge = TimeSpan.Zero; + lockId = null; + locked = false; + actionFlags = 0; + DateTime expires = DateTime.MinValue; + int timeout = 0; + string serializedItems = null; - // If record was not found, is expired or is locked, return. - if (expires < DateTime.Now || locked) - return result; + using (NpgsqlConnection dbConn = new NpgsqlConnection(m_connectionString)) + { + NpgsqlTransaction dbTrans = null; + try + { + dbConn.Open(); + dbTrans = dbConn.BeginTransaction(); - // If the actionFlags parameter is not InitializeItem, deserialize the stored SessionStateItemCollection - if (actionFlags == SessionStateActions.InitializeItem) - result = CreateNewStoreData(context, Convert.ToInt32(m_config.Timeout.TotalMinutes)); - else - result = new SessionStateStoreData(Deserialize(serializedItems), SessionStateUtility.GetSessionStaticObjects(context), Convert.ToInt32(m_config.Timeout.TotalMinutes)); + // Retrieve the current session item information and lock row + using (NpgsqlCommand dbCommand = dbConn.CreateCommand()) + { + dbCommand.CommandText = string.Format(CultureInfo.InvariantCulture, "SELECT \"Expires\", \"Timeout\", \"Locked\", \"LockId\", \"LockDate\", \"Data\", \"Flags\" FROM \"{0}\" WHERE \"SessionId\" = @SessionId AND \"ApplicationName\" = @ApplicationName FOR UPDATE", s_tableName); - if (lockRecord) - { - lockId = (int)lockId + 1; - // Obtain a lock to the record - using (NpgsqlCommand dbCommand = dbConn.CreateCommand()) - { - dbCommand.CommandText = string.Format(CultureInfo.InvariantCulture, "UPDATE \"{0}\" SET \"Locked\" = @Locked, \"LockId\" = @LockId,\"LockDate\" = @LockDate, \"Flags\" = @Flags WHERE \"SessionId\" = @SessionId AND \"ApplicationName\" = @ApplicationName", s_tableName); + dbCommand.Parameters.Add("@SessionId", NpgsqlDbType.Varchar, 80).Value = id; + dbCommand.Parameters.Add("@ApplicationName", NpgsqlDbType.Varchar, 255).Value = m_applicationName; - dbCommand.Parameters.Add("@Locked", NpgsqlDbType.Boolean).Value = true; - dbCommand.Parameters.Add("@LockId", NpgsqlDbType.Integer).Value = lockId; - dbCommand.Parameters.Add("@LockDate", NpgsqlDbType.TimestampTZ).Value = DateTime.Now; - dbCommand.Parameters.Add("@Flags", NpgsqlDbType.Integer).Value = 0; - dbCommand.Parameters.Add("@SessionId", NpgsqlDbType.Varchar, 80).Value = id; - dbCommand.Parameters.Add("@ApplicationName", NpgsqlDbType.Varchar, 255).Value = m_applicationName; + using (NpgsqlDataReader reader = dbCommand.ExecuteReader(System.Data.CommandBehavior.SingleRow)) + { + while (reader.Read()) + { + expires = reader.GetDateTime(0); + timeout = reader.GetInt32(1); + locked = reader.GetBoolean(2); + lockId = reader.GetInt32(3); + lockAge = DateTime.Now.Subtract(reader.GetDateTime(4)); - dbCommand.ExecuteNonQuery(); - } - } + if (!reader.IsDBNull(5)) + serializedItems = reader.GetString(5); - // Attempt to commit the transaction - dbTrans.Commit(); - } - catch (Exception e) - { - Trace.WriteLine(e.ToString()); + actionFlags = (SessionStateActions)reader.GetInt32(6); + } + reader.Close(); + } + } - if (dbTrans != null) - { - try - { - // Attempt to roll back the transaction - Trace.WriteLine(Properties.Resources.LogRollbackAttempt); - dbTrans.Rollback(); - } - catch (Exception re) - { - // Rollback failed - Trace.WriteLine(Properties.Resources.ErrRollbackFailed); - Trace.WriteLine(re.ToString()); - } - } + // If record was not found, is expired or is locked, return. + if (expires < DateTime.Now || locked) + return result; - throw new ProviderException(Properties.Resources.ErrOperationAborted); - } - finally - { - if (dbTrans != null) - dbTrans.Dispose(); + // If the actionFlags parameter is not InitializeItem, deserialize the stored SessionStateItemCollection + if (actionFlags == SessionStateActions.InitializeItem) + result = CreateNewStoreData(context, Convert.ToInt32(m_config.Timeout.TotalMinutes)); + else + result = new SessionStateStoreData(Deserialize(serializedItems), SessionStateUtility.GetSessionStaticObjects(context), Convert.ToInt32(m_config.Timeout.TotalMinutes)); - if (dbConn != null) - dbConn.Close(); - } + if (lockRecord) + { + lockId = (int)lockId + 1; + // Obtain a lock to the record + using (NpgsqlCommand dbCommand = dbConn.CreateCommand()) + { + dbCommand.CommandText = string.Format(CultureInfo.InvariantCulture, "UPDATE \"{0}\" SET \"Locked\" = @Locked, \"LockId\" = @LockId,\"LockDate\" = @LockDate, \"Flags\" = @Flags WHERE \"SessionId\" = @SessionId AND \"ApplicationName\" = @ApplicationName", s_tableName); - return result; - } - } + dbCommand.Parameters.Add("@Locked", NpgsqlDbType.Boolean).Value = true; + dbCommand.Parameters.Add("@LockId", NpgsqlDbType.Integer).Value = lockId; + dbCommand.Parameters.Add("@LockDate", NpgsqlDbType.TimestampTZ).Value = DateTime.Now; + dbCommand.Parameters.Add("@Flags", NpgsqlDbType.Integer).Value = 0; + dbCommand.Parameters.Add("@SessionId", NpgsqlDbType.Varchar, 80).Value = id; + dbCommand.Parameters.Add("@ApplicationName", NpgsqlDbType.Varchar, 255).Value = m_applicationName; - /// - /// Convert a SessionStateItemCollection into a Base64 string - /// - private static string Serialize(SessionStateItemCollection items) - { - if (items == null || items.Count < 1) - return string.Empty; + dbCommand.ExecuteNonQuery(); + } + } - using (MemoryStream mStream = new MemoryStream()) - { - using (BinaryWriter bWriter = new BinaryWriter(mStream)) - { - items.Serialize(bWriter); - bWriter.Close(); - } + // Attempt to commit the transaction + dbTrans.Commit(); + } + catch (Exception e) + { + Trace.WriteLine(e.ToString()); - return Convert.ToBase64String(mStream.ToArray()); - } - } + if (dbTrans != null) + { + try + { + // Attempt to roll back the transaction + Trace.WriteLine(Properties.Resources.LogRollbackAttempt); + dbTrans.Rollback(); + } + catch (Exception re) + { + // Rollback failed + Trace.WriteLine(Properties.Resources.ErrRollbackFailed); + Trace.WriteLine(re.ToString()); + } + } - /// - /// Convert a Base64 string into a SessionStateItemCollection - /// - /// - /// - private static SessionStateItemCollection Deserialize(string serializedItems) - { - SessionStateItemCollection sessionItems = new SessionStateItemCollection(); + throw new ProviderException(Properties.Resources.ErrOperationAborted); + } + finally + { + if (dbTrans != null) + dbTrans.Dispose(); - if (string.IsNullOrEmpty(serializedItems)) - return sessionItems; + if (dbConn != null) + dbConn.Close(); + } - using (MemoryStream mStream = new MemoryStream(Convert.FromBase64String(serializedItems))) - { - using (BinaryReader bReader = new BinaryReader(mStream)) - { - sessionItems = SessionStateItemCollection.Deserialize(bReader); - bReader.Close(); - } - } + return result; + } + } - return sessionItems; - } + /// + /// Convert a SessionStateItemCollection into a Base64 string + /// + private static string Serialize(SessionStateItemCollection items) + { + if (items == null || items.Count < 1) + return string.Empty; - /// - /// The ExpiredSessionDeletionTimer_Elapsed performs automatic session garbage collection by removing expired sessions from - /// the database. - /// - /// - /// - private void ExpiredSessionDeletionTimer_Elapsed(object source, System.Timers.ElapsedEventArgs e) - { - /* - * Determine mode of session garbage collection. If the session expire callback is disabled - * one may simple delete all expired session from the session table. If however the session expire callback - * is enabled, we need to load the session data for every expired session and invoke the expire callback - * for each of these sessions prior to deletion. - * Also check if an expire call back was actually defined. If m_expireCallback is null we also don't have to take - * the more expensive path where every session is enumerated while there's no real need to do so. - */ + using (MemoryStream mStream = new MemoryStream()) + { + using (BinaryWriter bWriter = new BinaryWriter(mStream)) + { + items.Serialize(bWriter); + bWriter.Close(); + } - if (m_enableExpireCallback && m_expireCallback != null) - InvokeExpireCallbackAndDeleteSession(); + return Convert.ToBase64String(mStream.ToArray()); + } + } - else - DeleteExpiredSessionsFromDatabase(); - } + /// + /// Convert a Base64 string into a SessionStateItemCollection + /// + /// + /// + private static SessionStateItemCollection Deserialize(string serializedItems) + { + SessionStateItemCollection sessionItems = new SessionStateItemCollection(); - /// - /// Load the session data for every expired session and invoke the expire callback - /// for each of these sessions prior to deletion. - /// - private void InvokeExpireCallbackAndDeleteSession() - { - Dictionary expiredSessions = null; + if (string.IsNullOrEmpty(serializedItems)) + return sessionItems; - // Start out by enumerating all expired sessions - using (NpgsqlConnection dbConn = new NpgsqlConnection(m_connectionString)) - { - using (NpgsqlCommand selectCommand = dbConn.CreateCommand()) - { - selectCommand.CommandText = string.Format(CultureInfo.InvariantCulture, "SELECT \"SessionId\", \"Data\" FROM \"{0}\" WHERE \"Expires\" < @Expires AND \"ApplicationName\" = @ApplicationName", s_tableName); + using (MemoryStream mStream = new MemoryStream(Convert.FromBase64String(serializedItems))) + { + using (BinaryReader bReader = new BinaryReader(mStream)) + { + sessionItems = SessionStateItemCollection.Deserialize(bReader); + bReader.Close(); + } + } - selectCommand.Parameters.Add("@Expires", NpgsqlDbType.TimestampTZ).Value = DateTime.Now; - selectCommand.Parameters.Add("@ApplicationName", NpgsqlDbType.Varchar, 255).Value = m_applicationName; + return sessionItems; + } - try - { - dbConn.Open(); - selectCommand.Prepare(); + /// + /// The ExpiredSessionDeletionTimer_Elapsed performs automatic session garbage collection by removing expired sessions from + /// the database. + /// + /// + /// + private void ExpiredSessionDeletionTimer_Elapsed(object source, System.Timers.ElapsedEventArgs e) + { + /* + * Determine mode of session garbage collection. If the session expire callback is disabled + * one may simple delete all expired session from the session table. If however the session expire callback + * is enabled, we need to load the session data for every expired session and invoke the expire callback + * for each of these sessions prior to deletion. + * Also check if an expire call back was actually defined. If m_expireCallback is null we also don't have to take + * the more expensive path where every session is enumerated while there's no real need to do so. + */ - using (NpgsqlDataReader reader = selectCommand.ExecuteReader()) - { - if (!reader.HasRows) - return; + if (m_enableExpireCallback && m_expireCallback != null) + InvokeExpireCallbackAndDeleteSession(); - expiredSessions = new Dictionary(reader.RecordsAffected); + else + DeleteExpiredSessionsFromDatabase(); + } - // Get session data from data reader and reconstruct session. - // NOTE: I'm not sure if I should pass any static objects to the constructor of the SessionStateStoreData class. - // Seems to me you should not since garbage collection is say highly unlikely to be run in an actual http context. - while (reader.Read()) - { - string sessionId = reader.GetString(0); - string serializedItems = reader.IsDBNull(1) ? null : reader.GetString(1); + /// + /// Load the session data for every expired session and invoke the expire callback + /// for each of these sessions prior to deletion. + /// + private void InvokeExpireCallbackAndDeleteSession() + { + Dictionary expiredSessions = null; - expiredSessions.Add(sessionId, new SessionStateStoreData(Deserialize(serializedItems), new HttpStaticObjectsCollection(), Convert.ToInt32(m_config.Timeout.TotalMinutes))); - } - } - } - catch (Exception ex) - { - Trace.WriteLine(ex.ToString()); + // Start out by enumerating all expired sessions + using (NpgsqlConnection dbConn = new NpgsqlConnection(m_connectionString)) + { + using (NpgsqlCommand selectCommand = dbConn.CreateCommand()) + { + selectCommand.CommandText = string.Format(CultureInfo.InvariantCulture, "SELECT \"SessionId\", \"Data\" FROM \"{0}\" WHERE \"Expires\" < @Expires AND \"ApplicationName\" = @ApplicationName", s_tableName); - if (dbConn != null) - dbConn.Close(); + selectCommand.Parameters.Add("@Expires", NpgsqlDbType.TimestampTZ).Value = DateTime.Now; + selectCommand.Parameters.Add("@ApplicationName", NpgsqlDbType.Varchar, 255).Value = m_applicationName; - throw new ProviderException(Properties.Resources.ErrOperationAborted); - } - } + try + { + dbConn.Open(); + selectCommand.Prepare(); - using (NpgsqlCommand deleteCommand = dbConn.CreateCommand()) - { - deleteCommand.CommandText = string.Format(CultureInfo.InvariantCulture, "DELETE FROM \"{0}\" WHERE \"SessionId\" = @SessionId AND \"ApplicationName\" = @ApplicationName", s_tableName); + using (NpgsqlDataReader reader = selectCommand.ExecuteReader()) + { + if (!reader.HasRows) + return; - deleteCommand.Parameters.Add("@SessionId", NpgsqlDbType.Varchar, 80); - deleteCommand.Parameters.Add("@ApplicationName", NpgsqlDbType.Varchar, 255).Value = m_applicationName; + expiredSessions = new Dictionary(reader.RecordsAffected); - NpgsqlTransaction dbTrans = null; + // Get session data from data reader and reconstruct session. + // NOTE: I'm not sure if I should pass any static objects to the constructor of the SessionStateStoreData class. + // Seems to me you should not since garbage collection is say highly unlikely to be run in an actual http context. + while (reader.Read()) + { + string sessionId = reader.GetString(0); + string serializedItems = reader.IsDBNull(1) ? null : reader.GetString(1); - try - { - deleteCommand.Prepare(); - dbTrans = dbConn.BeginTransaction(); + expiredSessions.Add(sessionId, new SessionStateStoreData(Deserialize(serializedItems), new HttpStaticObjectsCollection(), Convert.ToInt32(m_config.Timeout.TotalMinutes))); + } + } + } + catch (Exception ex) + { + Trace.WriteLine(ex.ToString()); - // Actually invoke session expire callback and delete session from the session table. - foreach (KeyValuePair expiredSession in expiredSessions) - { - // TODO: use async invocation insted? - m_expireCallback.Invoke(expiredSession.Key, expiredSession.Value); + if (dbConn != null) + dbConn.Close(); - deleteCommand.Parameters["@SessionId"].Value = expiredSession.Key; + throw new ProviderException(Properties.Resources.ErrOperationAborted); + } + } - deleteCommand.ExecuteNonQuery(); - } + using (NpgsqlCommand deleteCommand = dbConn.CreateCommand()) + { + deleteCommand.CommandText = string.Format(CultureInfo.InvariantCulture, "DELETE FROM \"{0}\" WHERE \"SessionId\" = @SessionId AND \"ApplicationName\" = @ApplicationName", s_tableName); - // Attempt to commit the transaction - dbTrans.Commit(); - } - catch (Exception ex) - { - Trace.WriteLine(ex.ToString()); + deleteCommand.Parameters.Add("@SessionId", NpgsqlDbType.Varchar, 80); + deleteCommand.Parameters.Add("@ApplicationName", NpgsqlDbType.Varchar, 255).Value = m_applicationName; - if (dbTrans != null) - { - try - { - // Attempt to roll back the transaction - Trace.WriteLine(Properties.Resources.LogRollbackAttempt); - dbTrans.Rollback(); - } - catch (Exception re) - { - // Rollback failed - Trace.WriteLine(Properties.Resources.ErrRollbackFailed); - Trace.WriteLine(re.ToString()); - } - } + NpgsqlTransaction dbTrans = null; - throw new ProviderException(Properties.Resources.ErrOperationAborted); - } - finally - { - if (dbTrans != null) - dbTrans.Dispose(); + try + { + deleteCommand.Prepare(); + dbTrans = dbConn.BeginTransaction(); - if (dbConn != null) - dbConn.Close(); - } - } - } - } + // Actually invoke session expire callback and delete session from the session table. + foreach (KeyValuePair expiredSession in expiredSessions) + { + // TODO: use async invocation insted? + m_expireCallback.Invoke(expiredSession.Key, expiredSession.Value); - /// - /// Delete all expired session from the session table. - /// - private void DeleteExpiredSessionsFromDatabase() - { - using (NpgsqlConnection dbConn = new NpgsqlConnection(m_connectionString)) - { - using (NpgsqlCommand dbCommand = dbConn.CreateCommand()) - { - dbCommand.CommandText = string.Format(CultureInfo.InvariantCulture, "DELETE FROM \"{0}\" WHERE \"Expires\" < @Expires AND \"ApplicationName\" = @ApplicationName", s_tableName); + deleteCommand.Parameters["@SessionId"].Value = expiredSession.Key; - dbCommand.Parameters.Add("@Expires", NpgsqlDbType.TimestampTZ).Value = DateTime.Now; - dbCommand.Parameters.Add("@ApplicationName", NpgsqlDbType.Varchar, 255).Value = m_applicationName; + deleteCommand.ExecuteNonQuery(); + } - NpgsqlTransaction dbTrans = null; + // Attempt to commit the transaction + dbTrans.Commit(); + } + catch (Exception ex) + { + Trace.WriteLine(ex.ToString()); - try - { - dbConn.Open(); - dbCommand.Prepare(); + if (dbTrans != null) + { + try + { + // Attempt to roll back the transaction + Trace.WriteLine(Properties.Resources.LogRollbackAttempt); + dbTrans.Rollback(); + } + catch (Exception re) + { + // Rollback failed + Trace.WriteLine(Properties.Resources.ErrRollbackFailed); + Trace.WriteLine(re.ToString()); + } + } - dbTrans = dbConn.BeginTransaction(); + throw new ProviderException(Properties.Resources.ErrOperationAborted); + } + finally + { + if (dbTrans != null) + dbTrans.Dispose(); - dbCommand.ExecuteNonQuery(); + if (dbConn != null) + dbConn.Close(); + } + } + } + } - // Attempt to commit the transaction - dbTrans.Commit(); - } - catch (Exception ex) - { - Trace.WriteLine(ex.ToString()); + /// + /// Delete all expired session from the session table. + /// + private void DeleteExpiredSessionsFromDatabase() + { + using (NpgsqlConnection dbConn = new NpgsqlConnection(m_connectionString)) + { + using (NpgsqlCommand dbCommand = dbConn.CreateCommand()) + { + dbCommand.CommandText = string.Format(CultureInfo.InvariantCulture, "DELETE FROM \"{0}\" WHERE \"Expires\" < @Expires AND \"ApplicationName\" = @ApplicationName", s_tableName); - if (dbTrans != null) - { - try - { - // Attempt to roll back the transaction - Trace.WriteLine(Properties.Resources.LogRollbackAttempt); - dbTrans.Rollback(); - } - catch (Exception re) - { - // Rollback failed - Trace.WriteLine(Properties.Resources.ErrRollbackFailed); - Trace.WriteLine(re.ToString()); - } - } + dbCommand.Parameters.Add("@Expires", NpgsqlDbType.TimestampTZ).Value = DateTime.Now; + dbCommand.Parameters.Add("@ApplicationName", NpgsqlDbType.Varchar, 255).Value = m_applicationName; - throw new ProviderException(Properties.Resources.ErrOperationAborted); - } - finally - { - if (dbTrans != null) - dbTrans.Dispose(); + NpgsqlTransaction dbTrans = null; - if (dbConn != null) - dbConn.Close(); - } - } - } - } + try + { + dbConn.Open(); + dbCommand.Prepare(); - #endregion - } + dbTrans = dbConn.BeginTransaction(); + + dbCommand.ExecuteNonQuery(); + + // Attempt to commit the transaction + dbTrans.Commit(); + } + catch (Exception ex) + { + Trace.WriteLine(ex.ToString()); + + if (dbTrans != null) + { + try + { + // Attempt to roll back the transaction + Trace.WriteLine(Properties.Resources.LogRollbackAttempt); + dbTrans.Rollback(); + } + catch (Exception re) + { + // Rollback failed + Trace.WriteLine(Properties.Resources.ErrRollbackFailed); + Trace.WriteLine(re.ToString()); + } + } + + throw new ProviderException(Properties.Resources.ErrOperationAborted); + } + finally + { + if (dbTrans != null) + dbTrans.Dispose(); + + if (dbConn != null) + dbConn.Close(); + } + } + } + } + + #endregion + } }