using System; using System.Collections.Specialized; using System.Configuration; using System.Configuration.Provider; using System.Data; using System.Data.Common; using System.Diagnostics; using System.IO; using System.Runtime.InteropServices; using System.Web; using System.Web.Configuration; using System.Web.Hosting; using System.Web.SessionState; namespace Example.Session.State.Provider { public abstract class ExampleSessionStateStore : SessionStateStoreProviderBase { private string connectionString; private readonly string defaultDescription; private readonly string defaultName; private string eventLog; private string exceptionMessage; private string pApplicationName; private SessionStateSection pConfig; private ConnectionStringSettings pConnectionStringSettings; private readonly DbProviderFactory provider; private bool pWriteExceptionsToEventLog; public string ApplicationName { get { return this.pApplicationName; } } public bool WriteExceptionsToEventLog { get { return this.pWriteExceptionsToEventLog; } set { this.pWriteExceptionsToEventLog = value; } } protected ExampleSessionStateStore(string defaultName, string defaultDescription, DbProviderFactory provider) { this.eventLog = "Application"; this.exceptionMessage = "An exception occurred with the Example Session State Provider."; this.defaultName = defaultName; this.defaultDescription = defaultDescription; this.provider = provider; } protected virtual void ApplyParameterInfo(DbParameter parameter, DbType dbType, object value) { parameter.set_DbType(dbType); parameter.set_Value(value); } private DbCommand CreateCommand(string commandText, DbConnection connection) { DbCommand sessionCommand = this.provider.CreateCommand(); sessionCommand.set_Connection(connection); sessionCommand.set_CommandText(commandText); return sessionCommand; } public override SessionStateStoreData CreateNewStoreData(HttpContext context, int timeout) { return new SessionStateStoreData(new SessionStateItemCollection(), SessionStateUtility.GetSessionStaticObjects(context), timeout); } private DbParameter CreateParameter(string name, DbType dbType, object value) { DbParameter sessionParameter = this.provider.CreateParameter(); sessionParameter.set_ParameterName(name); this.ApplyParameterInfo(sessionParameter, dbType, value); return sessionParameter; } private DbParameter CreateParameter(string name, DbType dbType, int size, object value) { DbParameter sessionParameter = this.provider.CreateParameter(); sessionParameter.set_ParameterName(name); sessionParameter.set_Size(size); this.ApplyParameterInfo(sessionParameter, dbType, value); return sessionParameter; } public override void CreateUninitializedItem(HttpContext context, string id, int timeout) { DbConnection sessionConnection = this.provider.CreateConnection(); sessionConnection.set_ConnectionString(this.connectionString); IDbCommand sessionCommand = this.CreateCommand("INSERT INTO aspnet_sessions (sessionid, applicationname, created, expires, lockdate, lockid, timeout, locked, sessionitems, flags) VALUES(:sessionid, :applicationname, :created, :expires, :lockdate, :lockid, :timeout, :locked, :sessionitems, :flags)", sessionConnection); sessionCommand.Parameters.Add(this.CreateParameter("sessionid", DbType.String, 80, id)); sessionCommand.Parameters.Add(this.CreateParameter("applicationname", DbType.String, 0xff, this.ApplicationName)); sessionCommand.Parameters.Add(this.CreateParameter("created", DbType.DateTime, DateTime.Now)); sessionCommand.Parameters.Add(this.CreateParameter("expires", DbType.DateTime, DateTime.Now.AddMinutes((double) timeout))); sessionCommand.Parameters.Add(this.CreateParameter("lockdate", DbType.DateTime, DateTime.Now)); sessionCommand.Parameters.Add(this.CreateParameter("lockid", DbType.Int32, 0)); sessionCommand.Parameters.Add(this.CreateParameter("timeout", DbType.Int32, timeout)); sessionCommand.Parameters.Add(this.CreateParameter("locked", DbType.Boolean, false)); sessionCommand.Parameters.Add(this.CreateParameter("sessionitems", DbType.Binary, 0, "")); sessionCommand.Parameters.Add(this.CreateParameter("flags", DbType.Int32, 1)); try { sessionConnection.Open(); sessionCommand.ExecuteNonQuery(); } catch (Exception exception) { if (this.WriteExceptionsToEventLog) { this.WriteToEventLog(exception, "CreateUninitializedItem"); throw new ProviderException(this.exceptionMessage); } throw exception; } finally { sessionConnection.Close(); } } private SessionStateStoreData Deserialize(HttpContext context, string serializedItems, int timeout) { MemoryStream sessionStream = new MemoryStream(Convert.FromBase64String(serializedItems)); SessionStateItemCollection sessionCollection = new SessionStateItemCollection(); if (sessionStream.Length > 0) { BinaryReader sessionReader = new BinaryReader(sessionStream); sessionCollection = SessionStateItemCollection.Deserialize(sessionReader); } return new SessionStateStoreData(sessionCollection, SessionStateUtility.GetSessionStaticObjects(context), timeout); } public override void Dispose() { } public override void EndRequest(HttpContext context) { } public override SessionStateStoreData GetItem(HttpContext context, string id, out bool locked, out TimeSpan lockAge, out object lockId, out SessionStateActions actionFlags) { return this.GetSessionStoreItem(false, context, id, 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) { return this.GetSessionStoreItem(true, context, id, out locked, out lockAge, out lockId, out actionFlags); } private SessionStateStoreData GetSessionStoreItem(bool lockRecord, HttpContext context, string id, out bool locked, out TimeSpan lockAge, out object lockId, out SessionStateActions actionFlags) { SessionStateStoreData sessionData = null; lockAge = TimeSpan.Zero; lockId = null; locked = false; actionFlags = SessionStateActions.None; DbConnection sessionConnection = this.provider.CreateConnection(); sessionConnection.set_ConnectionString(this.connectionString); IDbCommand sessionCommand = null; IDataReader sessionReader = null; string sessionText = ""; bool primaryFlag = false; bool secondaryFlag = false; int metric = 0; try { sessionConnection.Open(); if (lockRecord) { sessionCommand = this.CreateCommand("UPDATE aspnet_sessions SET locked = :locked1, lockdate = :lockdate WHERE sessionid = :sessionid AND LOWER(applicationname) = :applicationname AND locked = :locked2 AND expires > :expires", sessionConnection); sessionCommand.Parameters.Add(this.CreateParameter("locked1", DbType.Boolean, true)); sessionCommand.Parameters.Add(this.CreateParameter("lockdate", DbType.DateTime, DateTime.Now)); sessionCommand.Parameters.Add(this.CreateParameter("sessionid", DbType.String, 80, id)); sessionCommand.Parameters.Add(this.CreateParameter("applicationname", DbType.String, 0xff, this.ApplicationName.ToLower())); sessionCommand.Parameters.Add(this.CreateParameter("locked2", DbType.Boolean, false)); sessionCommand.Parameters.Add(this.CreateParameter("expires", DbType.DateTime, DateTime.Now)); if (sessionCommand.ExecuteNonQuery() == 0) { locked = true; } else { locked = false; } } sessionCommand = this.CreateCommand("SELECT expires, sessionitems, lockid, lockdate, flags, timeout FROM aspnet_sessions WHERE sessionid = :sessionid AND LOWER(applicationname) = :applicationname", sessionConnection); sessionCommand.Parameters.Add(this.CreateParameter("sessionid", DbType.String, 80, id)); sessionCommand.Parameters.Add(this.CreateParameter("applicationname", DbType.String, 0xff, this.ApplicationName.ToLower())); sessionReader = sessionCommand.ExecuteReader(CommandBehavior.SingleRow); while (sessionReader.Read()) { DateTime getTime = sessionReader.GetDateTime(0); if (getTime < DateTime.Now) { locked = false; currentTime = true; } else { primaryFlag = true; } sessionText = sessionReader.GetString(1); lockId = sessionReader.GetInt32(2); lockAge = DateTime.Now.Subtract(sessionReader.GetDateTime(3)); actionFlags = (SessionStateActions) sessionReader.GetInt32(4); metric = sessionReader.GetInt32(5); } sessionReader.Close(); if (secondaryFlag) { sessionCommand = this.CreateCommand("DELETE FROM aspnet_sessions WHERE sessionid = :sessionid AND LOWER(applicationname) = :applicationname", sessionConnection); sessionCommand.Parameters.Add(this.CreateParameter("sessionid", DbType.String, 80, id)); sessionCommand.Parameters.Add(this.CreateParameter("applicationname", DbType.String, 0xff, this.ApplicationName.ToLower())); sessionCommand.ExecuteNonQuery(); } if (!primaryFlag) { locked = false; } if (!primaryFlag || locked) { return sessionData; } lockId = ((int) lockId) + 1; sessionCommand = this.CreateCommand("UPDATE aspnet_sessions SET lockid = :lockid, flags = 0 WHERE sessionid = :sessionid AND LOWER(applicationname) = :applicationname", sessionConnection); sessionCommand.Parameters.Add(this.CreateParameter("lockid", DbType.Int32, lockId)); sessionCommand.Parameters.Add(this.CreateParameter("sessionid", DbType.String, 80, id)); sessionCommand.Parameters.Add(this.CreateParameter("applicationname", DbType.String, 0xff, this.ApplicationName.ToLower())); sessionCommand.ExecuteNonQuery(); if (actionFlags == SessionStateActions.InitializeItem) { return this.CreateNewStoreData(context, this.pConfig.Timeout.Minutes); } sessionData = this.Deserialize(context, sessionText, metric); } catch (Exception exception) { if (this.WriteExceptionsToEventLog) { this.WriteToEventLog(exception, "GetSessionStoreItem"); throw new ProviderException(this.exceptionMessage); } throw exception; } finally { if (sessionReader != null) { sessionReader.Close(); } sessionConnection.Close(); } return sessionData; } public override void Initialize(string name, NameValueCollection config) { if (config == null) { throw new ArgumentNullException("config"); } if ((name == null) || (name.Length == 0)) { name = this.defaultName; } if (string.IsNullOrEmpty(config["description"])) { config.Remove("description"); config.Add("description", this.defaultDescription); } base.Initialize(name, config); this.pApplicationName = config["ApplicationName"]; if (string.IsNullOrEmpty(this.pApplicationName)) { this.pApplicationName = HostingEnvironment.ApplicationVirtualPath; } config.Remove("ApplicationName"); System.Configuration.Configuration sessionConfiguration = WebConfigurationManager.OpenWebConfiguration(HostingEnvironment.ApplicationVirtualPath); this.pConfig = (SessionStateSection) sessionConfiguration.GetSection("system.web/sessionState"); this.pConnectionStringSettings = ConfigurationManager.ConnectionStrings[config["connectionStringName"]]; if (this.pConnectionStringSettings == null) { this.connectionString = ""; } else { this.connectionString = this.pConnectionStringSettings.ConnectionString.Trim(); } this.pWriteExceptionsToEventLog = false; if ((config["writeExceptionsToEventLog"] != null) && (config["writeExceptionsToEventLog"].ToUpper() == "TRUE")) { this.pWriteExceptionsToEventLog = true; } } public override void InitializeRequest(HttpContext context) { } public override void ReleaseItemExclusive(HttpContext context, string id, object lockId) { DbConnection sessionConnection = this.provider.CreateConnection(); sessionConnection.set_ConnectionString(this.connectionString); IDbCommand sessionCommand = this.CreateCommand("UPDATE aspnet_sessions SET locked = :locked, expires = :expires WHERE sessionid = :sessionid AND LOWER(applicationname) = :applicationname AND lockid = :lockid", sessionConnection); sessionCommand.Parameters.Add(this.CreateParameter("locked", DbType.Boolean, false)); sessionCommand.Parameters.Add(this.CreateParameter("expires", DbType.DateTime, DateTime.Now.AddMinutes((double) this.pConfig.Timeout.Minutes))); sessionCommand.Parameters.Add(this.CreateParameter("sessionid", DbType.String, 80, id)); sessionCommand.Parameters.Add(this.CreateParameter("applicationname", DbType.String, 0xff, this.ApplicationName.ToLower())); sessionCommand.Parameters.Add(this.CreateParameter("lockid", DbType.Int32, lockId)); try { sessionConnection.Open(); sessionCommand.ExecuteNonQuery(); } catch (Exception exception) { if (this.WriteExceptionsToEventLog) { this.WriteToEventLog(exception, "ReleaseItemExclusive"); throw new ProviderException(this.exceptionMessage); } throw exception; } finally { sessionConnection.Close(); } } public override void RemoveItem(HttpContext context, string id, object lockId, SessionStateStoreData item) { DbConnection sessionConnection = this.provider.CreateConnection(); sessionConnection.set_ConnectionString(this.connectionString); IDbCommand sessionCommand = this.CreateCommand("DELETE FROM aspnet_sessions WHERE sessionid = :sessionid AND LOWER(applicationname) = :applicationname AND lockid = :lockid", sessionConnection); sessionCommand.Parameters.Add(this.CreateParameter("sessionid", DbType.String, 80, id)); sessionCommand.Parameters.Add(this.CreateParameter("applicationname", DbType.String, 0xff, this.ApplicationName.ToLower())); sessionCommand.Parameters.Add(this.CreateParameter("lockid", DbType.Int32, lockId)); try { sessionConnection.Open(); sessionCommand.ExecuteNonQuery(); } catch (Exception exception) { if (this.WriteExceptionsToEventLog) { this.WriteToEventLog(exception, "RemoveItem"); throw new ProviderException(this.exceptionMessage); } throw exception; } finally { sessionConnection.Close(); } } public override void ResetItemTimeout(HttpContext context, string id) { DbConnection sessionConnection = this.provider.CreateConnection(); sessionConnection.set_ConnectionString(this.connectionString); IDbCommand sessionCommand = this.CreateCommand("UPDATE aspnet_sessions SET expires = :expires WHERE sessionid = :sessionid AND LOWER(applicationname) = :applicationname", sessionConnection); sessionCommand.Parameters.Add(this.CreateParameter("expires", DbType.DateTime, DateTime.Now.AddMinutes((double) this.pConfig.Timeout.Minutes))); sessionCommand.Parameters.Add(this.CreateParameter("sessionid", DbType.String, 80, id)); sessionCommand.Parameters.Add(this.CreateParameter("applicationname", DbType.String, 0xff, this.ApplicationName.ToLower())); try { sessionConnection.Open(); sessionCommand.ExecuteNonQuery(); } catch (Exception exception) { if (this.WriteExceptionsToEventLog) { this.WriteToEventLog(exception, "ResetItemTimeout"); throw new ProviderException(this.exceptionMessage); } throw exception; } finally { sessionConnection.Close(); } } private string Serialize(SessionStateItemCollection items) { MemoryStream sessionStream = new MemoryStream(); BinaryWriter sessionWriter = new BinaryWriter(sessionStream); if (items != null) { items.Serialize(sessionWriter); } sessionWriter.Close(); return Convert.ToBase64String(sessionStream.ToArray()); } public override void SetAndReleaseItemExclusive(HttpContext context, string id, SessionStateStoreData item, object lockId, bool newItem) { IDbCommand sessionCommand; string serText = this.Serialize((SessionStateItemCollection) item.Items); DbConnection sessionConnection = this.provider.CreateConnection(); sessionConnection.set_ConnectionString(this.connectionString); IDbCommand secSessionCommand = null; if (newItem) { secSessionCommand = this.CreateCommand("DELETE FROM aspnet_sessions WHERE sessionid = :sessionid AND LOWER(applicationname) = :applicationname AND expires < :expires", sessionConnection); secSessionCommand.Parameters.Add(this.CreateParameter("sessionid", DbType.String, 80, id)); secSessionCommand.Parameters.Add(this.CreateParameter("applicationname", DbType.String, 0xff, this.ApplicationName.ToLower())); secSessionCommand.Parameters.Add(this.CreateParameter("expires", DbType.DateTime, DateTime.Now)); sessionCommand = this.CreateCommand("INSERT INTO aspnet_sessions (sessionid, applicationname, created, expires, lockdate, lockid, timeout, locked, sessionitems, flags) VALUES(:sessionid, :applicationname, :created, :expires, :lockdate, :lockid, :timeout, :locked, :sessionitems, :flags)", sessionConnection); sessionCommand.Parameters.Add(this.CreateParameter("sessionid", DbType.String, 80, id)); sessionCommand.Parameters.Add(this.CreateParameter("applicationname", DbType.String, 0xff, this.ApplicationName)); sessionCommand.Parameters.Add(this.CreateParameter("created", DbType.DateTime, DateTime.Now)); sessionCommand.Parameters.Add(this.CreateParameter("expires", DbType.DateTime, DateTime.Now.AddMinutes((double) item.Timeout))); sessionCommand.Parameters.Add(this.CreateParameter("lockdate", DbType.DateTime, DateTime.Now)); sessionCommand.Parameters.Add(this.CreateParameter("lockid", DbType.Int32, 0)); sessionCommand.Parameters.Add(this.CreateParameter("timeout", DbType.Int32, item.Timeout)); sessionCommand.Parameters.Add(this.CreateParameter("locked", DbType.Boolean, false)); sessionCommand.Parameters.Add(this.CreateParameter("sessionitems", DbType.Binary, serText.Length, serText)); sessionCommand.Parameters.Add(this.CreateParameter("flags", DbType.Int32, 0)); } else { sessionCommand = this.CreateCommand("UPDATE aspnet_sessions SET expires = :expires, sessionitems = :sessionitems, locked = :locked WHERE sessionid = :sessionid AND LOWER(applicationname) = :applicationname AND lockid = :lockid", sessionConnection); sessionCommand.Parameters.Add(this.CreateParameter("expires", DbType.DateTime, DateTime.Now.AddMinutes((double) item.Timeout))); sessionCommand.Parameters.Add(this.CreateParameter("sessionitems", DbType.Binary, serText.Length, serText)); sessionCommand.Parameters.Add(this.CreateParameter("locked", DbType.Boolean, false)); sessionCommand.Parameters.Add(this.CreateParameter("sessionid", DbType.String, 80, id)); sessionCommand.Parameters.Add(this.CreateParameter("applicationname", DbType.String, 0xff, this.ApplicationName.ToLower())); sessionCommand.Parameters.Add(this.CreateParameter("lockid", DbType.Int32, lockId)); } try { sessionConnection.Open(); if (secSessionCommand != null) { secSessionCommand.ExecuteNonQuery(); } sessionCommand.ExecuteNonQuery(); } catch (Exception exception) { if (this.WriteExceptionsToEventLog) { this.WriteToEventLog(exception, "SetAndReleaseItemExclusive"); throw new ProviderException(this.exceptionMessage); } throw exception; } finally { sessionConnection.Close(); } } public override bool SetItemExpireCallback(SessionStateItemExpireCallback expireCallback) { return false; } private void WriteToEventLog(Exception e, string action) { EventLog log = new EventLog(); log.Source = this.defaultName; log.Log = this.eventLog; string exceptionText = "An exception was thrown.\n\n"; exceptionText = exceptionText + "Action: " + action + "\n\n"; exceptionText = exceptionText + "Exception: " + e.ToString(); log.WriteEntry(exceptionText); } } }