Thursday, December 24, 2009

Session with Style

Sloppy code, unfortunately, is ubiquitous. Sometimes it's due to laziness; other times to ignorance, as in: "it never even occurred to me to do it otherwise." I'd put the example I use in this post as one of the latter for most people.

So you want to store something in Session? Easy.

Session["MyString"] = "this is a string I'm storing in session."

Then, somewhere else in your application you do:

MyLabel.Text = (string)Session["MyString"]; 

Maybe "sloppy" is too strong a word. But it certainly isn't slick--for a number of reasons: casting, remembering keys, key typos, overwriting existing keys, etc.

An incrementally better solution might be to use an enumeration or constants for the keys. A step beyond that would be to put all of your Session-related information into a single class and store that in Session.

Let's bump it up a level. Let's take this to 11. Here's a much better way.

Start with a base class you can reuse in any of your ASP.NET applications.

using System;
using System.Web;

namespace MyNamespace
{
    public class SessionInfo<Subclass>
        where Subclass : SessionInfo<Subclass>, new()
    {
        private static string Key
        {
            get { return typeof(SessionInfo<Subclass>).FullName; }
        }

        private static Subclass Value
        {
            get { return (Subclass) HttpContext.Current.Session[Key]; }
            set { HttpContext.Current.Session[Key] = value; }
        }

        public static Subclass Current
        {
            get
            {
                var instance = Value;
                if (instance == null)
                    lock (typeof(Subclass)) // not ideal to lock on a type -- but it'll work
                    {
                        // standard lock double-check
                        instance = Value;
                        if (instance == null)
                            Value = instance = new Subclass();
                    }
                return instance;
            }
        }
    }
}

Now, create a class specific to your current application.

public class MySessionInfo : SessionInfo<MySessionInfo>
{
   public string SomeString { get; set; }
}

To use it in your application, simply do:

// set the value
MySessionInfo.Current.SomeString = "this is a string I'm storing in session.";

// get the value
MyLabel.Text = MySessionInfo.Current.SomeString;

Of course, you can put more than just strings into your subclass. You can create separate subclasses for use in unrelated parts of your application, or you can create a single class (if the design calls for it) to store everything you'll need to have in Session. Perhaps something like:

public class MySessionInfo : SessionInfo<MySessionInfo>
{
   public WebSiteMember WebSiteMember { get; set; }
   public ShoppingCart ShoppingCart { get; set; }
   public BrowseHistory BrowseHistory { get; set; }
}

Just remember that if you'll be using anything other than in-process session state, you'll have to make your class serializable.

7 comments:

  1. This solution does not work if you want to access the Session value in a declarative fashion as a Select Parameter for a Data Source.

    ReplyDelete
  2. I think the lock(typeof(Subclass)) should be replaced with lock(Session.SyncRoot).

    Then you'll only be blocking that Session rather than any thread that happens to be doing anything with the Subclass type.

    ReplyDelete
  3. what if you need to store 2 instances of the same type? for instance, a start date and end date for a selection.

    ReplyDelete
  4. Dan, not a problem. Just create a subclass with the two properties needed: StartDate and EndDate.

    ReplyDelete
  5. Isnt this over architecting

    ReplyDelete
  6. Nice design but the code where it will be used will not be unit testable in pure isolation.

    ReplyDelete