A better way of working with HTTPContext.Session in MVC
Posted on January 1, 2012One of the problems I’ve been having is passing session in and out of my controllers, helpers, and even models in some cases. Its been messy, and thats before I even thought about how it would work when it came to testing…
This week I sat down and came up with what I think is a pretty clean and neat implementation when it comes to dealing with session.
As always, it starts with an interface.
public interface ISessionCache
{
T Get<T>(string key);
void Set<T>(string key, T item);
bool contains(string key);
void clearKey(string key);
T singleTon<T>(String key, getStuffAction<T> actionToPerform);
}
Now it all looks pretty standard, except for that last method. That last method, singleTon, was created simply because I’m sick and tired of using session in my get properties or wherever using the singleton pattern. Using generics and a delegate, I have made a base class to do my singleton checks for me.
Here is my aptly named delegate, its basically any method that returns T.
public delegate T getStuffAction<T>();
I marked the base class as abstract and also marked all of the ISessionCache members as abstract except for the singleTon method. Like i said, the methods nothing fancy, it just saves me some keystrokes, and cleans up my code a bit.
public abstract class BaseSessionCache : ISessionCache
{
public abstract T Get<T>(string key);
public abstract void Set<T>(string key, T item);
public abstract bool contains(string key);
public abstract void clearKey(string key);
public virtual T singleTon<T>(String key, getStuffAction<T> actionToPerform)
{
if (!contains(key))
{
Set<T>(key, actionToPerform());
}
return Get<T>(key);
}
}
Next is an in memory implementation I can use for testing purposes. It basically adds and gets stuff out of a dictionary.
public class InMemorySessionCache : BaseSessionCache
{
Dictionary<String, Object> _col;
public InMemorySessionCache()
{
_col = new Dictionary<string, object>();
}
public T Get<T>(string key)
{
return (T)_col[key];
}
public void Set<T>(string key, T item)
{
_col.Add(key, item);
}
public bool contains(string key)
{
if (_col.ContainsKey(key))
{
return true;
}
return false;
}
public void clearKey(string key)
{
if (contains(key))
{
_col.Remove(key);
}
}
}
And finally is my actual HTTPContext.Session implementation that I use in my live site.
public class HttpContextSessionCache : BaseSessionCache
{
private readonly HttpContext _context;
public HttpContextSessionCache()
{
_context = HttpContext.Current;
}
public T Get<T>(string key)
{
object value = _context.Session[key];
return value == null ? default(T) : (T)value;
}
public void Set<T>(string key, T item)
{
_context.Session[key] = item;
}
public bool contains(string key)
{
if (_context.Session[key] != null)
{
return true;
}
return false;
}
public void clearKey(string key)
{
_context.Session[key] = null;
}
}
Once again, pretty standard stuff.
Now lets see it all in action.
Public class FunController : Controller
{
private ISessionCache _cache;
public String SomethingInSession
{
get
{
//the traditional singleton pattern
if (!_cache.contains("somekey"))
{
_cache.Set<String>("somekey", "somevalue");
}
return _cache.Get<String>("somekey");
}
set
{
_cache.Set<String>("somekey", value);
}
}
public String SomethingElseInSession
{
get
{
//using the singleton method, saves you some lines of code.
return _cache.singleTon<String>("somekey", () => "somevalue");
}
set
{
_cache.Set<String>("somekey", value);
}
}
public FunController(ISessionCache cache)
{
_cache = cache;
}
public ActionResult Index()
{
//check if session contains someKey
if (_cache.contains("someKey"))
{
//if not, add a list of string
_cache.Set<List<String>>("someKey", new List<string>() { "im in session", "me too" });
}
//somekey is a strongly typed list of string
var t = _cache.Get<List<String>>("someKey");
//clear it out of session
_cache.clearKey("someKey");
return new EmptyResult();
}
}
Now you can hook the interface up to your dependency injection and inject session into your controllers, helpers and whatever else you need without worrying about sacrificing your testability. Just wire up your test methods to use the in memory implementation and off you go. Yay…
Categories: Asp.net MVC
Recent Comments
Close block