In this blog I would like to share the SybaseSessionState I have implemented. Thanks to this article http://www.codeproject.com/KB/aspnet/ASPNET_session_in_MySql.aspx in CodeProject. I only did a slight changes to user Sybase as the data store.
In your Web.config, you needed to use the following configuration.
<sessionState cookieless="false" regenerateExpiredSessionId="true" mode="Custom" customProvider="SybaseSessionProvider" >
<providers>
<add name="SybaseSessionProvider"
type="SybaseSessionStateStore"
connectionStringName="YourConnectionStringName"
writeExceptionsToEventLog="false" />
</providers>
</sessionState>
Please see the code
/// <summary>
/// <remarks>
/// The following table has to be created
/// CREATE TABLE sessions
/// (
/// SessionId varchar(80) NOT NULL,
/// ApplicationName varchar(255) NOT NULL,
/// Created datetime NOT NULL,
/// Expires datetime NOT NULL,
/// LockDate datetime NOT NULL,
/// LockId int NOT NULL,
/// Timeout int NOT NULL,
/// Locked bit NOT NULL,
/// SessionItems text,
/// Flags int NOT NULL,
/// CONSTRAINT PKSessions PRIMARY KEY (SessionId, ApplicationName)
/// )
/// </remarks>
/// </summary>
public sealed class SybaseSessionStateStore : SessionStateStoreProviderBase
{
private SessionStateSection pConfig = null;
private string connectionString;
private ConnectionStringSettings pConnectionStringSettings;
private string eventSource = "SybaseSessionStateStore";
private string eventLog = "Application";
private string exceptionMessage = "An exception occurred. Please contact your administrator.";
private string pApplicationName;
private bool pWriteExceptionsToEventLog = false;
/// <summary>
/// If false, exceptions are thrown to the caller. If true, exceptions are written to the event log.
/// </summary>
public bool WriteExceptionsToEventLog
{
get { return pWriteExceptionsToEventLog; }
set { pWriteExceptionsToEventLog = value; }
}
/// <summary>
/// The ApplicationName property is used to differentiate sessions in the data source by application.
/// </summary>
public string ApplicationName
{
get { return pApplicationName; }
}
/// <summary>
///
/// </summary>
/// <param name="name"></param>
/// <param name="config"></param>
public override void Initialize(string name, NameValueCollection config)
{
// Initialize values from web.config.
if (config == null)
throw new ArgumentNullException("config");
if (name == null || name.Length == 0)
name = "SybaseSessionStateStore";
if (String.IsNullOrEmpty(config["description"]))
{
config.Remove("description");
config.Add("description", "Sybase Session State Store provider");
}
// Initialize the abstract base class.
base.Initialize(name, config);
// Initialize the ApplicationName property.
pApplicationName = System.Web.Hosting.HostingEnvironment.ApplicationVirtualPath;
// Get <sessionState> configuration element.
System.Configuration.Configuration cfg = WebConfigurationManager.OpenWebConfiguration(ApplicationName);
pConfig = (SessionStateSection)cfg.GetSection("system.web/sessionState");
// Initialize connection string.
pConnectionStringSettings = ConfigurationManager.ConnectionStrings[config["connectionStringName"]];
if (pConnectionStringSettings == null || pConnectionStringSettings.ConnectionString.Trim() == "")
{
throw new ProviderException("Connection string cannot be blank.");
}
connectionString = pConnectionStringSettings.ConnectionString;
// Initialize WriteExceptionsToEventLog
pWriteExceptionsToEventLog = false;
if (config["writeExceptionsToEventLog"] != null)
{
if (config["writeExceptionsToEventLog"].ToUpper() == "TRUE")
pWriteExceptionsToEventLog = true;
}
}
/// <summary>
///
/// </summary>
public override void Dispose()
{
}
/// <summary>
///
/// </summary>
/// <param name="expireCallback"></param>
/// <returns></returns>
public override bool SetItemExpireCallback(SessionStateItemExpireCallback expireCallback)
{
return false;
}
/// <summary>
///
/// </summary>
/// <param name="context"></param>
/// <param name="id"></param>
/// <param name="item"></param>
/// <param name="lockId"></param>
/// <param name="newItem"></param>
public override void SetAndReleaseItemExclusive(HttpContext context, string id, SessionStateStoreData item, object lockId, bool newItem)
{
// Serialize the SessionStateItemCollection as a string.
string sessItems = Serialize((SessionStateItemCollection)item.Items);
AseConnection conn = new AseConnection(connectionString);
AseCommand cmd;
AseCommand deleteCmd = null;
if (newItem)
{
// AseCommand to clear an existing expired session if it exists.
deleteCmd = new AseCommand("DELETE FROM sessions WHERE SessionId = @SessionId AND ApplicationName = @ApplicationName AND Expires < @Expires", conn);
deleteCmd.Parameters.Add("@SessionId", AseDbType.VarChar, 80).Value = id;
deleteCmd.Parameters.Add("@ApplicationName", AseDbType.VarChar, 255).Value = ApplicationName;
deleteCmd.Parameters.Add("@Expires", AseDbType.DateTime).Value = DateTime.Now;
// AseCommand to insert the new session item.
cmd = new AseCommand("INSERT INTO sessions (SessionId, ApplicationName, Created, Expires, LockDate, LockId, Timeout, Locked, SessionItems, Flags) Values(@SessionId, @ApplicationName, @Created, @Expires, @LockDate, @LockId, @Timeout, @Locked, @SessionItems, @Flags)", conn);
cmd.Parameters.Add("@SessionId", AseDbType.VarChar, 80).Value = id;
cmd.Parameters.Add("@ApplicationName", AseDbType.VarChar, 255).Value = ApplicationName;
cmd.Parameters.Add("@Created", AseDbType.DateTime).Value = DateTime.Now;
cmd.Parameters.Add("@Expires", AseDbType.DateTime).Value = DateTime.Now.AddMinutes((Double)item.Timeout);
cmd.Parameters.Add("@LockDate", AseDbType.DateTime).Value = DateTime.Now;
cmd.Parameters.Add("@LockId", AseDbType.Integer).Value = 0;
cmd.Parameters.Add("@Timeout", AseDbType.Integer).Value = item.Timeout;
cmd.Parameters.Add("@Locked", AseDbType.Integer).Value = 0;
cmd.Parameters.Add("@SessionItems", AseDbType.Text).Value = sessItems;
cmd.Parameters.Add("@Flags", AseDbType.Integer).Value = 0;
}
else
{
// AseCommand to update the existing session item.
cmd = new AseCommand("UPDATE sessions SET Expires = @Expires, SessionItems = @SessionItems, Locked = @Locked WHERE SessionId = @SessionId AND ApplicationName = @ApplicationName AND LockId = @LockId", conn);
cmd.Parameters.Add("@Expires", AseDbType.DateTime).Value = DateTime.Now.AddMinutes((Double)item.Timeout);
cmd.Parameters.Add("@SessionItems", AseDbType.Text).Value = sessItems;
cmd.Parameters.Add("@Locked", AseDbType.Integer).Value = 0;
cmd.Parameters.Add("@SessionId", AseDbType.VarChar, 80).Value = id;
cmd.Parameters.Add("@ApplicationName", AseDbType.VarChar, 255).Value = ApplicationName;
cmd.Parameters.Add("@LockId", AseDbType.Integer).Value = lockId;
}
try
{
conn.Open();
if (deleteCmd != null)
deleteCmd.ExecuteNonQuery();
cmd.ExecuteNonQuery();
}
catch (AseException e)
{
if (WriteExceptionsToEventLog)
{
WriteToEventLog(e, "SetAndReleaseItemExclusive");
throw new ProviderException(exceptionMessage);
}
else
throw e;
}
finally
{
conn.Close();
}
}
/// <summary>
///
/// </summary>
/// <param name="context"></param>
/// <param name="id"></param>
/// <param name="locked"></param>
/// <param name="lockAge"></param>
/// <param name="lockId"></param>
/// <param name="actionFlags"></param>
/// <returns></returns>
public override SessionStateStoreData GetItem(HttpContext context, string id, out bool locked, out TimeSpan lockAge, out object lockId, out SessionStateActions actionFlags)
{
return GetSessionStoreItem(false, context, id, out locked, out lockAge, out lockId, out actionFlags);
}
/// <summary>
///
/// </summary>
/// <param name="context"></param>
/// <param name="id"></param>
/// <param name="locked"></param>
/// <param name="lockAge"></param>
/// <param name="lockId"></param>
/// <param name="actionFlags"></param>
/// <returns></returns>
public override SessionStateStoreData GetItemExclusive(HttpContext context, string id, out bool locked, out TimeSpan lockAge, out object lockId, out SessionStateActions actionFlags)
{
return GetSessionStoreItem(true, context, id, out locked, out lockAge, out lockId, out actionFlags);
}
/// <summary>
/// GetSessionStoreItem is called by both the GetItem and GetItemExclusive methods. GetSessionStoreItem retrieves the
/// session data from the data source. If the lockRecord parameter is true (in the case of GetItemExclusive), then GetSessionStoreItem
/// locks the record and sets a new LockId and LockDate.
/// </summary>
/// <param name="lockRecord"></param>
/// <param name="context"></param>
/// <param name="id"></param>
/// <param name="locked"></param>
/// <param name="lockAge"></param>
/// <param name="lockId"></param>
/// <param name="actionFlags"></param>
/// <returns></returns>
private SessionStateStoreData GetSessionStoreItem(bool lockRecord, HttpContext context, string id, out bool locked, out TimeSpan lockAge, out object lockId, out SessionStateActions actionFlags)
{
// Initial values for return value and out parameters.
SessionStateStoreData item = null;
lockAge = TimeSpan.Zero;
lockId = null;
locked = false;
actionFlags = 0;
AseConnection conn = new AseConnection(connectionString);
AseCommand cmd = null;
AseDataReader reader = null;
// Datetime to check if current session item is expired.
DateTime expires;
// String to hold serialized SessionStateItemCollection.
string serializedItems = "";
// True if a record is found in the database.
bool foundRecord = false;
// True if the returned session item is expired and needs to be deleted.
bool deleteData = false;
// Timeout value from the data store.
int timeout = 0;
try
{
conn.Open();
// lockRecord is true when called from GetItemExclusive and false when called from GetItem.
// Obtain a lock if possible. Ignore the record if it is expired.
if (lockRecord)
{
cmd = new AseCommand("UPDATE sessions SET Locked = @Locked1, LockDate = @LockDate WHERE SessionId = @SessionId AND ApplicationName = @ApplicationName AND Locked = @Locked2 AND Expires > @Expires", conn);
cmd.Parameters.Add("@Locked1", AseDbType.Integer).Value = 1;
cmd.Parameters.Add("@LockDate", AseDbType.DateTime).Value = DateTime.Now;
cmd.Parameters.Add("@SessionId", AseDbType.VarChar, 80).Value = id;
cmd.Parameters.Add("@ApplicationName", AseDbType.VarChar, 255).Value = ApplicationName;
cmd.Parameters.Add("@Locked2", AseDbType.Integer).Value = 0;
cmd.Parameters.Add("@Expires", AseDbType.DateTime).Value = DateTime.Now;
if (cmd.ExecuteNonQuery() == 0)
{
// No record was updated because the record was locked or not found.
locked = true;
}
else
{
// The record was updated.
locked = false;
}
}
// Retrieve the current session item information.
cmd = new AseCommand("SELECT Expires, SessionItems, LockId, LockDate, Flags, Timeout FROM sessions WHERE SessionId = @SessionId AND ApplicationName = @ApplicationName", conn);
cmd.Parameters.Add("@SessionId", AseDbType.VarChar, 80).Value = id;
cmd.Parameters.Add("@ApplicationName", AseDbType.VarChar, 255).Value = ApplicationName;
// Retrieve session item data from the data source.
reader = cmd.ExecuteReader(CommandBehavior.SingleRow);
while (reader.Read())
{
expires = reader.GetDateTime(0);
if (expires < DateTime.Now)
{
// The record was expired. Mark it as not locked.
locked = false;
// The session was expired. Mark the data for deletion.
deleteData = true;
}
else
foundRecord = true;
serializedItems = reader.GetString(1);
lockId = reader.GetInt32(2);
lockAge = DateTime.Now.Subtract(reader.GetDateTime(3));
actionFlags = (SessionStateActions)reader.GetInt32(4);
timeout = reader.GetInt32(5);
}
reader.Close();
// If the returned session item is expired,
// delete the record from the data source.
if (deleteData)
{
cmd = new AseCommand("DELETE FROM sessions WHERE SessionId = @SessionId AND ApplicationName = @ApplicationName", conn);
cmd.Parameters.Add("@SessionId", AseDbType.VarChar, 80).Value = id;
cmd.Parameters.Add("@ApplicationName", AseDbType.VarChar, 255).Value = ApplicationName;
cmd.ExecuteNonQuery();
}
// The record was not found. Ensure that locked is false.
if (!foundRecord)
locked = false;
// If the record was found and you obtained a lock, then set
// the lockId, clear the actionFlags,
// and create the SessionStateStoreItem to return.
if (foundRecord && !locked)
{
lockId = (int)lockId + 1;
cmd = new AseCommand("UPDATE sessions SET LockId = @LockId, Flags = 0 WHERE SessionId = @SessionId AND ApplicationName = @ApplicationName", conn);
cmd.Parameters.Add("@LockId", AseDbType.Integer).Value = lockId;
cmd.Parameters.Add("@SessionId", AseDbType.VarChar, 80).Value = id;
cmd.Parameters.Add("@ApplicationName", AseDbType.VarChar, 255).Value = ApplicationName;
cmd.ExecuteNonQuery();
// If the actionFlags parameter is not InitializeItem,
// deserialize the stored SessionStateItemCollection.
if (actionFlags == SessionStateActions.InitializeItem)
item = CreateNewStoreData(context, pConfig.Timeout.Minutes);
else
item = Deserialize(context, serializedItems, timeout);
}
}
catch (AseException e)
{
if (WriteExceptionsToEventLog)
{
WriteToEventLog(e, "GetSessionStoreItem");
throw new ProviderException(exceptionMessage);
}
else
throw e;
}
finally
{
if (reader != null) { reader.Close(); }
conn.Close();
}
return item;
}
/// <summary>
/// Serialize is called by the SetAndReleaseItemExclusive method to convert the SessionStateItemCollection into a Base64 string to
/// be stored in an Access Memo field.
/// </summary>
/// <param name="items"></param>
/// <returns></returns>
private string Serialize(SessionStateItemCollection items)
{
MemoryStream ms = new MemoryStream();
BinaryWriter writer = new BinaryWriter(ms);
if (items != null)
items.Serialize(writer);
writer.Close();
return Convert.ToBase64String(ms.ToArray());
}
/// <summary>
/// DeSerialize is called by the GetSessionStoreItem method to convert the Base64 string stored in the Access Memo field to a
/// SessionStateItemCollection.
/// </summary>
/// <param name="context"></param>
/// <param name="serializedItems"></param>
/// <param name="timeout"></param>
/// <returns></returns>
private SessionStateStoreData Deserialize(HttpContext context, string serializedItems, int timeout)
{
MemoryStream ms = new MemoryStream(Convert.FromBase64String(serializedItems));
SessionStateItemCollection sessionItems = new SessionStateItemCollection();
if (ms.Length > 0)
{
BinaryReader reader = new BinaryReader(ms);
sessionItems = SessionStateItemCollection.Deserialize(reader);
}
return new SessionStateStoreData(sessionItems,
SessionStateUtility.GetSessionStaticObjects(context),
timeout);
}
/// <summary>
///
/// </summary>
/// <param name="context"></param>
/// <param name="id"></param>
/// <param name="lockId"></param>
public override void ReleaseItemExclusive(HttpContext context, string id, object lockId)
{
AseConnection conn = new AseConnection(connectionString);
AseCommand cmd = new AseCommand("UPDATE sessions SET Locked = 0, Expires = @Expires WHERE SessionId = @SessionId AND ApplicationName = @ApplicationName AND LockId = @LockId", conn);
cmd.Parameters.Add("@Expires", AseDbType.DateTime).Value = DateTime.Now.AddMinutes(pConfig.Timeout.Minutes);
cmd.Parameters.Add("@SessionId", AseDbType.VarChar, 80).Value = id;
cmd.Parameters.Add("@ApplicationName", AseDbType.VarChar, 255).Value = ApplicationName;
cmd.Parameters.Add("@LockId", AseDbType.Integer).Value = lockId;
try
{
conn.Open();
cmd.ExecuteNonQuery();
}
catch (AseException e)
{
if (WriteExceptionsToEventLog)
{
WriteToEventLog(e, "ReleaseItemExclusive");
throw new ProviderException(exceptionMessage);
}
else
throw e;
}
finally
{
conn.Close();
}
}
/// <summary>
///
/// </summary>
/// <param name="context"></param>
/// <param name="id"></param>
/// <param name="lockId"></param>
/// <param name="item"></param>
public override void RemoveItem(HttpContext context, string id, object lockId, SessionStateStoreData item)
{
AseConnection conn = new AseConnection(connectionString);
AseCommand cmd = new AseCommand("DELETE FROM sessions WHERE SessionId = @SessionId AND ApplicationName = @ApplicationName AND LockId = @LockId", conn);
cmd.Parameters.Add("@SessionId", AseDbType.VarChar, 80).Value = id;
cmd.Parameters.Add("@ApplicationName", AseDbType.VarChar, 255).Value = ApplicationName;
cmd.Parameters.Add("@LockId", AseDbType.Integer).Value = lockId;
try
{
conn.Open();
cmd.ExecuteNonQuery();
}
catch (AseException e)
{
if (WriteExceptionsToEventLog)
{
WriteToEventLog(e, "RemoveItem");
throw new ProviderException(exceptionMessage);
}
else
throw e;
}
finally
{
conn.Close();
}
}
/// <summary>
///
/// </summary>
/// <param name="context"></param>
/// <param name="id"></param>
/// <param name="timeout"></param>
public override void CreateUninitializedItem(HttpContext context, string id, int timeout)
{
AseConnection conn = new AseConnection(connectionString);
AseCommand cmd = new AseCommand("INSERT INTO sessions (SessionId, ApplicationName, Created, Expires, LockDate, LockId, Timeout, Locked, SessionItems, Flags) Values(@SessionId, @ApplicationName, @Created, @Expires, @LockDate, @LockId, @Timeout, @Locked, @SessionItems, @Flags)", conn);
cmd.Parameters.Add("@SessionId", AseDbType.VarChar, 80).Value = id;
cmd.Parameters.Add("@ApplicationName", AseDbType.VarChar, 255).Value = ApplicationName;
cmd.Parameters.Add("@Created", AseDbType.DateTime).Value = DateTime.Now;
cmd.Parameters.Add("@Expires", AseDbType.DateTime).Value = DateTime.Now.AddMinutes((Double)timeout);
cmd.Parameters.Add("@LockDate", AseDbType.DateTime).Value = DateTime.Now;
cmd.Parameters.Add("@LockId", AseDbType.Integer).Value = 0;
cmd.Parameters.Add("@Timeout", AseDbType.Integer).Value = timeout;
cmd.Parameters.Add("@Locked", AseDbType.Integer).Value = 0;
cmd.Parameters.Add("@SessionItems", AseDbType.Text).Value = "";
cmd.Parameters.Add("@Flags", AseDbType.Integer).Value = 1;
try
{
conn.Open();
cmd.ExecuteNonQuery();
}
catch (AseException e)
{
if (WriteExceptionsToEventLog)
{
WriteToEventLog(e, "CreateUninitializedItem");
throw new ProviderException(exceptionMessage);
}
else
throw e;
}
finally
{
conn.Close();
}
}
/// <summary>
///
/// </summary>
/// <param name="context"></param>
/// <param name="timeout"></param>
/// <returns></returns>
public override SessionStateStoreData CreateNewStoreData(HttpContext context, int timeout)
{
return new SessionStateStoreData(new SessionStateItemCollection(), SessionStateUtility.GetSessionStaticObjects(context), timeout);
}
/// <summary>
///
/// </summary>
/// <param name="context"></param>
/// <param name="id"></param>
public override void ResetItemTimeout(HttpContext context,
string id)
{
AseConnection conn = new AseConnection(connectionString);
AseCommand cmd = new AseCommand("UPDATE sessions SET Expires = @Expires WHERE SessionId = @SessionId AND ApplicationName = @ApplicationName", conn);
cmd.Parameters.Add("@Expires", AseDbType.DateTime).Value = DateTime.Now.AddMinutes(pConfig.Timeout.Minutes);
cmd.Parameters.Add("@SessionId", AseDbType.VarChar, 80).Value = id;
cmd.Parameters.Add("@ApplicationName", AseDbType.VarChar, 255).Value = ApplicationName;
try
{
conn.Open();
cmd.ExecuteNonQuery();
}
catch (AseException e)
{
if (WriteExceptionsToEventLog)
{
WriteToEventLog(e, "ResetItemTimeout");
throw new ProviderException(exceptionMessage);
}
else
throw e;
}
finally
{
conn.Close();
}
}
/// <summary>
///
/// </summary>
/// <param name="context"></param>
public override void InitializeRequest(HttpContext context)
{
}
/// <summary>
///
/// </summary>
/// <param name="context"></param>
public override void EndRequest(HttpContext context)
{
}
/// <summary>
/// WriteToEventLog
/// This is a helper function that writes exception detail to the event log. Exceptions are written to the event log as a security
/// measure to ensure private database details are not returned to browser. If a method does not return a status or Boolean
/// indicating the action succeeded or failed, the caller also throws a generic exception.
/// </summary>
/// <param name="e"></param>
/// <param name="action"></param>
private void WriteToEventLog(Exception e, string action)
{
EventLog log = new EventLog();
log.Source = eventSource;
log.Log = eventLog;
string message = "An exception occurred communicating with the data source.\n\n";
message += "Action: " + action + "\n\n";
message += "Exception: " + e.ToString();
log.WriteEntry(message);
}
}