Update:
- The Sourcecode is now “copy & pastable”.
- I wrote an newer version which has more dependencies, but is suitable for UnitTesting (How to make ASP.net session state testable)
After reading Why Should You Wrap Your ASP.NET Session Object by Shahar A I thought the Community is ready for another step of encapsulation – and this is what I want to share.
We all know about the problems storing data in untyped dictionaries using some key, so I spare you.
This is how I want to store and retrieve session variables:
public class MySessionVariables
{
// just an example, pls use Asp.Net Membership instead…
public static readonly SessionVariable<string[]> UserGroups
= new SessionVariable<string[]>(“UserGroups”);
}
// Fill the UserGroups after Login, for example
public void DeleteDatabase()
{
if (MySessionVariables.UserGroups.HasValue &&
MySessionVariables.UserGroups.Value.Contains(“Admin”))
MyApplication.DeleteDatabase();
}
I would really like to not have a key to store the value, but I couldn't avoid. I thought about creating a new Guid per class instance. But this won't work when you run multiple Applications using some network session state. If someone has an idea on this, please share!
Initializer
Sometimes I need a dictionary or a List<T> to cache some values. So in order to avoid checking wether my SessionVariable has any value before accessing it I would like it to be initialized automatically on first use.
public class MySessionVariables
{
public static readonly SessionVariable<List<string>> MarkedArticles
= new SessionVariable<List<string>>(“MarkedArticles”, () => new List<string>());
}
If you're not using C# 3 yet, you could easily add a delegate instead of using Func<T>.
1: public delegate T CreateInitialValueHandler();
ValueOrDefault that gives back default(T) if no value is assigned yet, while Value throws an exception if neither a value nor an initializer is available.Free Source Code
/// <summary>
/// Wrapper class for <see cref=”HttpContext.Session”/>.
/// Author: Lars Corneliussen
/// Source: http://startbigthinksmall.wordpress.com/2008/05/14/how-to-wrap-the-aspnet-session-state/
/// </summary>
/// <typeparam key=”T”>
/// The type of the value to be stored.
/// </typeparam>
public class SessionVariable<T>
{
private readonly string key;
private readonly Func<T> initializer;
/// <summary>
/// Initializes a new session variable.
/// </summary>
/// <param name=”key”>
/// The key to use for storing the value in the session.
/// </param>
public SessionVariable(string key)
{
if (string.IsNullOrEmpty(key))
throw new ArgumentNullException("key");
this.key = GetType() + key;
}
/// <summary>
/// Initializes a new session variable with a initializer.
/// </summary>
/// <param name=”key”>
/// The key to use for storing the value in the session.
/// </param>
/// <param name=”initializer”>
/// A function that is called in order to create a
/// default value per session.
/// </param>
public SessionVariable(string key, Func<T> initializer) : this(key)
{
if (initializer == null)
throw new ArgumentNullException("initializer");
this.initializer = initializer;
}
private object GetInternalValue(bool initializeIfNessesary)
{
HttpSessionState session = CurrentSession;
var value = session[key];
if (value == null && initializeIfNessesary
&& initializer != null)
session[key] = value = initializer();
return value;
}
private static HttpSessionState CurrentSession
{
get
{
var current = HttpContext.Current;
if (current == null)
throw new InvalidOperationException(
"No HttpContext is not available.");
var session = current.Session;
if (session == null)
throw new InvalidOperationException(
"No Session available on current HttpContext.");
return session;
}
}
/// <summary>
/// Indicates wether there is a value present or not.
/// </summary>
public bool HasValue
{
get { return GetInternalValue(false) != null; }
}
/// <summary>
/// Sets or gets the value in the current session.
/// </summary>
/// <exception cref=”InvalidOperationException”>
/// If you try to get a value while none is set.
/// Use <see cref=”ValueOrDefault”/> for safe access.
/// </exception>
public T Value
{
get
{
object v = GetInternalValue(true);
if (v == null)
throw new InvalidOperationException(
"The session does not contain any value for ‘"
+ key + "‘.");
return (T) v;
}
set { CurrentSession[key] = value; }
}
/// <summary>
/// Gets the value in the current session or if
/// none is available <c>default(T)</c>.
/// </summary>
public T ValueOrDefault
{
get
{
object v = GetInternalValue(true);
if (v == null)
return default(T);
return (T)v;
}
}
/// <summary>
/// Clears the value in the current session.
/// </summary>
public void Clear()
{
CurrentSession.Remove(key);
}
}

This is pretty damn sweet. I’ll definitely be using this. Tho, I’ll probably change a couple things.
Throwing when no valid HttpContext is available is not the best thing IMHO. It makes testing more difficult, and can result in users seeing error pages when the server is recycling.
I’d suggest returning a dummy session (I usually do this for the cache; you have to use Activator.CreateInstance since the cache doesn’t have a public constructor), as this is acceptable 99.999% of the time, and avoids having to deal with handling that very rare exception wherever you access a session variable.
Thanks!
Idea: ValueOrDefault, could also return default(T) or new T() when session is not available.. and Value could call the init-method everytime, if session is not available, but I would’nt like it that much.
I, myself wrap HttpContext.Current.Application, HttpContext.Current.Items and HttpContext.Current.Session in a IContextStorage with three IDictionaries…
This is then overidable with “dummy”-implementations using the ThreadVariable. So in a test, i simply use:
using(ContextStorage.Override(new StaticStorage())
{
// anny code using ContextStorage.Session
// or SessionVariable
// or ApplicationVariable
// or RequestVariable
}
But anyhow, I rather like exceptions then unexpected undefined behaviour.
Good idea! I always hated not having them strongly typed.
Generics rock.
[...] 19, 2008 by larscorneliussen If you’ve read my Article about How to wrap the ASP.net session state I want to show you how to make the wrapper testable using the Free .Net Ambient Context Pattern [...]
Wonderful, I have done so much work but not extensively at session like you.
Keep it up.
Joggee
This looks good.
A few questions/comments though:
In your example, you first call MySessionVariables.UserGroups.HasValue and then MySessionVariables.UserGroups.Value.Contains(“Admin”), both calls result in call to GetInternalValue. Why not have a local variable (private T m_value) to store the retrived value in, then you would only have to do that once every http request.
Also, there seems to me be a threading issue. If the SessionVariable value is null, and somebody tries to read it (GetInternalValue(true)), while another thread is trying to set a new value. Then the new value could be replaced by default(T) value.
I dont know if it make sense, or is even an issue.
Hi Egil,
1) “The first answer i gave here was wront”
But still, Value throws an exception if nothing is defined. So you had to use ValueOrDefault and then check against the default value wich is null for reference types and a specific one per struct or value type.
2) Normally this should be locked, but IMHO it is not necessary in this case. All HttpHandlers that implement IRequiresSessionState are locked to one request per session. They enter the lock in SessionStateModule.BeginAcquireState() and leave it in SessionStateModule.OnReleaseState().
You can avoid this by implementing IReadOnlySessionState in your IHttpHandler or Page (wich implicitly implements IHttpHandler while the PageBuilder usually compiles IRequiresSessionState into *.aspx files).
If I’m wrong, please correct me.
Admittedly, I am not that familiar with session management in asp.net, so my concern with threading issues might not be valid.
Could you elaborate a bit on your StaticStorage() class. Is it a common place where you have Session, Application and Current.Items available?
Yes, but then i had to post a solution with 6 or 7 classes plus unit tests. People rather copy paste then download solutions.
I just removed the dependency to HttpContext by wrapping it. Using my ThreadVariable (older post about ambient context pattern) i can then override the Dictionaries for Session, Applications and Items.
public interface IContextStorage
{
IDictionary Items { get; }
IDictionary Session { get; }
IDictionary Application { get; }
}
For Session and Application I made a wrapper to HttpSessionState and HttpApplicationState that implements IDictionary.
So my current implementations are HttpContextStorage as default, StaticContextStorage for Tests and ServiceSessionContextStorage that delegates to specific Dictionaries for Session and Application in WCF Services.
Interested in the code? Someone else, too?
Yes please. Do share.
Ok. some day
But in ASP.NET MVC Microsoft came with some abstraction for HttpContext eg. IHttpContext.
As I know they wont change HttpContext.Current to IHttpContext though.
Indeed, I am looking very much forward to working with MVC on my next pet project.
Lars, I wonder what you think of this (http://pastebin.com/f66bcfb3c) approach, using Extension methods to give you a strongly typed experience with the SessionState.
I put that code together a few weeks ago.
Well, I would like to avoid strings as keys. While this is not possible i prefer to only specify the key once and then forget about it whereever i can. Putting it in a constant and then using the Extension Methods could be OK, but IMHO not that nice.
I also don’t like to access HttpContext.Current.Session in order to be able to call the ExtensionMethod, because then I have to handle the possible null references every time I use a value. The initializer and testability (http://startbigthinksmall.wordpress.com/2008/05/19/how-make-code-that-uses-my-sessionvariable-testable) are to other arguments for SessionVariable
Nice implementation you have there
I realise it’s a bit off-topic, but can I just ask – what tool is it you use to provide your code syntax highlighting?
It is a wordpress plugin:
[ sourcecode language="c#" ]
// your code…
[ /sourcecode ]
Use it without spaces between [ and sourcecode.
They utilize the google syntax highlighter:
http://code.google.com/p/syntaxhighlighter
I also wrote a WLW Plugin that helps me posting the code – WLW does not handle correctly
Thanks mate
Hi,
thanks for sharing your idea.
We implemented it in our project, but when we published our web site on iis 7.0 and two or more users enter concurrently last user’s data overwrite the others users data, mainly when the session variable was use inside of objectdatasource.
could you please help me?,
thank’s
Jaimir G.
Hi,
I’m sorry you encounter problems with the implementation. Shared session state is the worst bug I can imagine.
It seems to be a caching problem. I don’t know the objectdatasource well, but could it be, that you have to add SessionParameters to it?
<asp:ObjectDataSource ID="ods" runat="server" OldValuesParameterFormatString="original_{0}" SelectMethod="GetStuff" TypeName="RM.Stuff"> <SelectParameters> <asp:SessionParameter SessionField='SESSION_PAREMETER_NAME' Name="somename" Type="Int32" /> </SelectParameters>Could you post your code or drop me a mail? (lars(ät)corneliussen.de)
Hello Lars,
We’ve already found our problem. A load analysis reported that the method “CurrentSession” executed thousands of times. To reduce the impact, we declared the variable “session” as static at class level, and that’s the reason that data of different users were mixed. So, we decided to move to your original previous version of “CurrentSession”
Thanks for your answer and collaboration.
Jaimir G.