Making session variables a little easier to work with
It’s been quiet on here for a while – that’s because I’ve been warped back to WebForms land where everything is craziness and it takes you half a day just to figure out wth is going on because you have to work with logic spread across a half dozen code behind files that all run to a couple thousand lines (no exaggeration!).
The app I am currently working on uses session variables a lot, and doesn’t clean them up very often, leading to odd behaviors due to stale state (among other things).
The first thing I do when I come across a page using session variables is extract them all into properties at the top of the page – this ensures those ugly string indexers are only in 2 places (the get and set) and you only have to write the cast once. The problem I find with this is that to figure out if the session variable has a value or clean up the session variable I still have to address it using the string indexer, sure I can wrap that in a simple method call so I only have the indexer in 3 or 4 places, but when you have a page with 6 session variables, this starts to add a lot of cruft to the code behind. What I really wanted was a simple way of adding a few methods to the property so I could do something like this…
int newValue = 0;
if( MySessionVar.HasValue )
{
newValue += MySessionVar;
}
MySessionVar.Clear();
With a little bit of poking around, some generics and a simple implicit cast, I ended up with this:
using System;
using System.Web;
namespace Blog.Code
{
///<summary>
/// Wraps a number of convenience methods around session variables allowing for a more fluent
/// expression of the intent.
///</summary>
///<typeparam name="T">The type of the value to store in the session.</typeparam>
public class SessionVariable<T> : IEquatable<SessionVariable<T>>
{
private readonly T _tempValueStore;
private readonly string _sessionKey;
///<summary>
/// The stored value or the default value for the type
/// if there is no value associated with the sessionkey
///</summary>
public T Value
{
get
{
object value = HttpContext.Current.Session[_sessionKey];
if(value == null)
{
return default( T );
}
return (T)value;
}
}
///<summary>
/// Indicates if there is a value stored.
///</summary>
public bool HasValue
{
get
{
return HttpContext.Current.Session[ _sessionKey ] != null;
}
}
///<summary>
/// Creates a new SessionVariable to access the value stored in <paramref name="sessionKey"/>
///</summary>
///<param name="sessionKey">The key used to access the value in the session</param>
public SessionVariable( string sessionKey )
{
_sessionKey = sessionKey;
}
///<summary>
/// Creates a new SessionVariable to store the value provided.
///</summary>
///<param name="value">The value to store.</param>
/// <remarks>
/// Value is not stored until <see cref="Store"/> is called.
/// </remarks>
public SessionVariable( T value )
{
_tempValueStore = value;
}
///<summary>
/// Stores the value in the session using the key specified.
///</summary>
///<param name="sessionKey">The key used to store the value.</param>
public void Store(string sessionKey)
{
HttpContext.Current.Session[ sessionKey ] = _tempValueStore;
}
///<summary>
/// Removes the entry from the session.
///</summary>
public void Clear()
{
HttpContext.Current.Session.Remove( _sessionKey );
}
///<summary>
/// Implicitly converts the specified value into a SessionVariable instance.
///</summary>
///<param name="value">The value to convert.</param>
///<returns>A SessionVariable instance holding the specified value.</returns>
public static implicit operator SessionVariable<T>(T value)
{
return new SessionVariable<T>( value );
}
///<summary>
/// Implicitly converts the SessionVariable back to the type of the wrapped value.
///</summary>
///<param name="sessionVariable">The SessionVariable to convert</param>
///<returns>The wrapped value.</returns>
public static implicit operator T(SessionVariable<T> sessionVariable)
{
return sessionVariable.Value;
}
public override string ToString()
{
return Value.ToString();
}
public bool Equals( SessionVariable<T> sessionVariable )
{
if ( sessionVariable == null )
{
return false;
}
return Equals( Value, sessionVariable.Value );
}
public override bool Equals( object obj )
{
if ( ReferenceEquals( this, obj ) )
{
return true;
}
return Equals( obj as SessionVariable<T> );
}
public override int GetHashCode()
{
return Value.GetHashCode();
}
}
}
Which can be used like this:
protected SessionVariable<string> SuccessMessage
{
get
{
return new SessionVariable<string>("SuccessMessage");
}
set
{
value.Store("SuccessMessage");
}
}
The only caveat is when accessing methods on the object you are wrapping, you have to use 1 of 2 approaches to get at the underlying value…
// Directly access the underlying value SuccessMessage.Value.Split(...); // Assign the wrapped value to a temporary variable to force the implicit cast string successMessage = SuccessMessage; successMessage.Split(...);
As the session is really just a Dictionary<string, object> you could probably make this work for any dictionary with a few simple changes, however I didn’t need that flexibility so I leave it as an exercise for the future.
This is pretty cool!!!
When showing how to “directly access the underying value”, don’t you mean the following?
SuccessMessage.Value.Split(…);
Yeh, thanks for catching that one Ori
I really like this approach, but have one question.
I’m attempting to put the definition of my session variables inside of a class called “MySession”, so that I can access them like this:
MySession.UserID
But I can’t get it to work. Any suggestions? Am I trying to do something inappropriate?
For example:
public class MySession {
public SessionVariable SuccessMessage
{
get
{
return new SessionVariable(“SuccessMessage”);
}
set
{
value.Store(“SuccessMessage”);
}
}
}
And then reference as MySession.SuccessMessage.
It seems that if I do that, the Intellisense should pick up SuccessMessage and it does not.
OK, so sorry to bother y’all. Complete user error, forgot to instantiate my class.