On using Session state

[Update: fixed a couple of syntactic bugs that Luke Hutteman was kind enough to point out. Not sure why, but DasBlog's mail-to-weblog feature likes to eat underscores. Wish I could say that it likes to eat downcasts, too, but that one's my fault]

I’ve seen this pattern a lot in ASP.NET applications:

//Inside some function…

newOrder.CreditCard      = (CreditCardInfo)HttpContext.Current.Session[CREDITCARD_KEY];
newOrder.BillingAddress  = (AddressInfo)HttpContext.Current.Session[BILLING_KEY];
newOrder.ShippingAddress = (AddressInfo)HttpContext.Current.Session[SHIPPING_KEY];

That is, using Session state as this big unorganized mess of keys and values seems to be pretty pervasive. Sure it works, but the problem is that it’s slow. At least it’s not as fast as it could be. All that downcasting is expensive, and all those hash lookups add up. Why make your system do all that unnecessary work when it doesn’t have to?

A better way is to do this involves giving some structure to Session. Instead of sticking all your data in a loosely-typed hashtable type thing, create a strongly typed container to hold your data and stick a single instance of that class in Session under a well-known key. A sample class that would cover the lines of code above would look like this:

public class SessionDataContainer
{
       public SessionStore()
       {
       }

       public CreditCardInfo  CREDITCARD;
       public BillingAddress  BILLING;
       public ShippingAddress SHIPPING;
}

Inside of the Session Start() event handler inside global.asax, initialize a single instance of SessionDataContainer by doing the following:

protected void Session_Start(Object sender, EventArgs e)
{
       Session[ CONTAINER_KEY ] = new SessionDataContainer();
}

This would allow the code shown above to be rewritten as follows:

SessionDataContainer sessionState =
     (SessionDataContainer)HttpContext.Current.Session[ CONTAINER KEY ];

newOrder.CreditCard      = sessionState.CREDITCARD;
newOrder.BillingAddress  = sessionState.BILLING;
newOrder.ShippingAddress = sessionState.SHIPPING;

This replaces n downcasts and n hash lookups with 1 downcast and 1 hash lookup. Plus, it has the additional advantage of being type-checked at compile time and therefore less prone to bugs. Clearly the biggest payoff comes when n (the number of unique session keys being accessed) is significantly larger than 1, but there are performance gains to be seen from n as small as 2 -- and the compile-time type checking is just icing on the cake.

#1 Steve Maine on 11.21.2003 at 12:30 PM

I wouldn't read too much in to my choice of concrete examples. In this case, the code I used was lifted verbatim from the Microsoft .NET PetShop reference implementation.I wasn't really trying to make a point about using Session state in the context of an ordering secenario...more that whenever you have a loosely-typed collection (in this case, Session state) whose contents are deterministic and well-known at compile-time, it makes better sense to encapsulate those contents into a strongly-typed container to minimize the number of downcasts and hash lookups needed to access your data.However, in the specific case above, you make some good points. Kinda scary when you think that this code came from a "reference implementation"... :)

#2 Mark on 11.24.2003 at 5:35 AM

dotnetjunkies.com/.../1189.aspx

#3 Scott on 11.24.2003 at 10:43 PM

Forgive me for not doing the legwork myself...Does the Session collection have the same serializeable limitations that the ViewState does?Do the objects you store in them have to implement ISerializeable and have public properties?

#4 Luke Hutteman on 11.25.2003 at 6:14 AM

I doubt if the performance benefits of this approach would be significant in any application - your bottleneck is bound to be elsewhere.That being said, I do think this is a good approach, but for reasons of maintainability instead of performance, as it saves you from having to deal with a plethora of typecasts and lookup keys across your code-base.btw, you forgot the underscore in "Session Start()" and the cast to SessionDataContainer inSessionDataContainer sessionState = HttpContext.Current.Session[ CONTAINER KEY ];

#5 gerrard on 12.03.2003 at 3:50 PM

I'm a bit confused. Regardless of whether or not you use session, why wouldn't you write your app to store this data in a structure? It's much easier to read, and makes it apparent that this group of data should be treated as a unit. Or are you suggesting that you build a surrogate structure for even unrelated data which you want to store in session?