Wednesday, April 28, 2010

Prim and Proper way to ASP.NET Sessions

After being to the web domain, I felt a bit uneasy by the way Sessions were handled in ASP.NET; more or less the session class was like a universal adapter, where you can plug in data-types from primitive types to reference types. At first glance, it might seem like a boon, but the devil is in the details; as soon as the development phase scales up, this very handy feature could turn out to be a nightmare, if not properly dealt with.

Here I had compiled some common issues, while dealing with raw ASP.NET session objects.
  • Session Name Typo - This is a common issue, where a session name say "AutomationID" created in PageA.aspx when accessed from PageB.aspx, might be inaccessible due to session name typo, when a developer accesses the session, by keying in "Aut0mationID". Here you can find a small typo, where zero is entered in, instead of the character "o". These sort of issues could eat up productive time and could even end up eating up others too. The straight forward solution for this is to use constants instead of using string literals. I recommend to create string constants to set and get values from session.
Here is the prim and proper solution for "Session Name Typo" issue.
public const string AUTOMATION_ID = "AutomationID";
//PageA.aspx
Session[AUTOMATION_ID] = 12;

//PageB.aspx
int Val = (int)Session[AUTOMATION_ID];
  • Session Datatype mismatch - This could happen when a value of type Int64 is set to a session and while accessing the same, the developer might unknowingly casts it to an 'int' (which in Int32 by default), which could lead to overflow bugs. The solution is to use strongly typed Session classes, which we are about to explore.
  • public const string AUTOMATION_ID = "AutomationID";
    //PageA.aspx
    Int64 AutID = 123456789000;
    Session[AUTOMATION_ID] = AutID;
    
    //PageB.aspx
    int Val = (int)Session[AUTOMATION_ID]; //Outputs -1097262584
  • Session Overwriting - This could occur when a developer unknowingly assigns a session name, which is already used by another session for keeping a value of different type. These could be sorted out through strongly typed session class.
  • public const string USER_ID = "UserID";
    //PageA.aspx
    Int64 UserID = 123456789000;
    Session[USER_ID] = UserID ;
    
    //PageB.aspx
    string username = "abcxyz";
    Session[USER_ID] = username; //Here UserID of type In64 is replaced with string.


By now a question might be popping-up in you mind like, why couldn't Microsoft provide a straightforward solution to this problem. Well before waiting for Microsoft to come out with a solution, lets jump into our own customized solution.


So now lets get on to the interesting part of creating a SessionParams class. Here we are going to create a static class named SessionParams for accessing the ASP.NET session in a intuitive object oriented way.


  public static class SessionParams
    {
        #region "Constants"
            private const string SESSION_PREFIX = "SESSION_PARAM_";
            private const string S_USERNAME = SESSION_PREFIX  + "USER_NAME";
            private const string S_AGE = SESSION_PREFIX  + "AGE";
        #endregion

        #region "Private"

        private static T GetParam<T>(string ParamName)
        {
            T Result = default(T);

            if (HttpContext.Current != null &&
                HttpContext.Current.Session != null)
            {
                if (HttpContext.Current.Session[ParamName] != null)
                    Result = (T)HttpContext.Current.Session[ParamName];
            }

            return Result;
        }

        private static void SetParam<T>(string ParamName, T Val)
        {
            if (HttpContext.Current != null &&
                HttpContext.Current.Session != null)
            {
                HttpContext.Current.Session[ParamName] = Val;
            }
        }

        #endregion

        #region "Constructor"
        
        static SessionParams()
        {
            //Place any initialization code here.
        }

        #endregion

        #region "Public Session Params"

        //Sample Session properties  
        public static string UserName
        {
            get { return GetParam<string>(S_USERNAME); }
            set { SetParam(S_USERNAME , value); }
        }

        //Sample Session properties
        public static int Age
        {
            get { return GetParam<int>(S_AGE); }
            set { SetParam(S_AGE , value); }
        }

        #endregion        
    }


What you just saw is a strongly typed session proxy class, which could be just accessed like any other static classes from ASP.NET pages and usercontrols. If you dig into the "SessionParams" class, you could see its a simple session wrapper. 


Now lets dissect the SessionParams class and see, what goes inside into this SessionParams class. The first thing is that, the SessionParams class is logically split to three regions like Constants, Private methods and Public properties.


The Constants region contains the names of session variables. While the Private methods region contains two generic methods namely "GetParam" and "SetParam" of both which receives a generic parameter. These two methods provide the core functionality of retrieving and assigning values to and from session variables; under the hood every public properties for this SessionParams is wrapping around these generic methods. Finally the third region the Public Properties. This is the place where you can access and define new session parameters for accessing, session values from ASP.NET usercontrols and pages.


The SessionParams class we had just discussed is having two sample session variables "Username" and "Age". Here's how to get and set values from the sample session variables.
string NameVal = "AlphaBeta";
int AgeVal = 25;

SessionParams.UserName = NameVal;
SessionParams.Age = AgeVal;

Response.Write(SessionParams.UserName);
Response.Write(SessionParams.Age);

Lets create a new param named "IsRegistered" of type "bool" inside the SessionParams class
  • First of all, you need to create a constant inside the constants region.
private const string S_IS_REGISTERED = SESSION_PREFIX  + "IS_REGISTERED";
  • There after, create a public property named "IsRegistered" of type bool inside region "Public Session Params" like the one below.
public static bool IsRegistered
        {
            get { return GetParam<bool>(S_IS_REGISTERED); }
            set { SetParam(S_IS_REGISTERED , value); }
        }
  • Thats it; now you new property "IsRegistered" is ready for action. Here's how its put into use.
SessionParams.IsRegistered = true;
Response.Write(SessionParams.IsRegistered);

Now we have a strongly typed SessionParams class in place, for interacting with ASP.NET sessions, which is to iron out the problems previously discussed. 


If you happen to encounter any issues with the SessionParams implementation, please let me know.


No comments: