Click here to Skip to main content
15,868,141 members
Articles / Web Development / ASP.NET

Wrap Those Session Variables!

Rate me:
Please Sign up or sign in to vote.
4.51/5 (18 votes)
12 Mar 2007CPOL8 min read 102K   37   42
Encapsulating the details of session variable management.

Introduction

ASP.NET applications typically have to deal with session variables. It's not uncommon to have to deal with lots of them, and if you're not careful, you can have the variable names sprinkled liberally throughout your code. When it comes time to refactor those session variables--such as when one needs to be renamed or becomes obsolete--this becomes a maintenance headache. Further, you may find yourself constantly trying to remember what the string key is that references a particular session variable.

In large, multi-developer projects, this can be especially problematic. If there's no centralized location for managing session variables, they may not be documented very well, if at all. How is a particular session variable used? What is its data type? When is it meaningful? When was it introduced? What pieces of the software use it? Is it still used?

Without some discipline, session variables can quickly become unmanageable. In applications with large numbers of users, where the amount of memory consumed by your sessions is important, this can be a serious consideration. You need to be able to quickly deprecate obsolete variables and identify every point in the code that uses them. You need to be able to quickly change the name of a variable without impacting the entire system or using kludgy search-and-replace features.

Enter encapsulation and information hiding, key principles of object oriented programming. In this article, we'll talk about a class that encapsulates the details of working with session variables. It will completely abstract away the details of the key names, so you don't have to remember them, or sprinkle those string constants throughout your code. Further, it will provide strongly-typed properties that do away with the need to perform type casting at the point of call. It will allow you to determine if you even have a valid session object to begin with, before you attempt to query it for information. And, it will allow you to quickly deprecate variables in the event that one becomes obsolete. Finally, it will make it easy to refactor the string key should you need to do so.

Using the Code

The class we're talking about is called MySession. Because it's so small and straightforward, I've included its full code right here.

VB
Option Explicit On
Option Strict On

Imports System.Web.SessionState 

''' <summary>
'''    Encapsulates the details of managing session variables.
''' </summary>
Friend NotInheritable Class MySession   

   Private Const UserIdKey As String = "UserId"   

   Private Sub New()  
   End Sub  
   
   ''' <summary>
   '''    Returns the current Session object, if any.
   ''' </summary>
   Private Shared ReadOnly Property Session As HttpSessionState     
      Get       
         Return HttpContext.Current.Session     
      End Get   
   End Property   
   
   ''' <summary>
   '''    Returns True if a valid Session object exists.
   ''' </summary>
   Public Shared ReadOnly Property Exists() As Boolean    
      Get      
         Return Not Session Is Nothing    
      End Get  
   End Property  
   
   ''' <summary>
   '''    Gets the ID of the user who is currently logged onto
   '''    the system.
   ''' </summary>
   Public Shared Property UserId As Integer
      Get       
         Return DirectCast(Session(UserIdKey), Integer)     
      End Get     
      Set(ByVal value As Integer)       
         Session(UserIdKey) = value     
      End Set   
   End Property 
   
End Class

To use the class, you simply need to drop it into your application, remove the sample property, and add one property for every session variable you use. Make sure that each property you define is marked Public and Shared. Then, in your code, instead of directly accessing session variables, you reference the variable through the MySession class's strongly-typed property.

The following are worthy points of note regarding this class:

  • The class has Friend scope. It can't be accessed outside of the Web application. That's just sound security.
  • The Private constructor prevents instantiation. Since all of the other methods are marked Shared, it makes no sense to instantiate an instance of this class.
  • The Session property returns an instance of the current session. This property is marked Private and Shared, and completely hides how we're getting the session information.
  • The Exists property returns True if we have a valid session object. This helps us to avoid calling the various properties on the MySession class if there isn't a valid session to begin with. This is frequently the case in an exception handler.
  • The UserId property (a sample property only) is marked Public and Shared, and is responsible for getting a value out of the session and putting it into the session. It uses a class-scope constant to reference the session variable, ensuring that both the getter and setter reference the same session variable. Further, the property is strongly typed. When you call this property, the session variable is already converted to the appropriate data type.

Using a class like this provides a host of benefits:

  1. The session variables are now centralized in one place. This provides an immediate payback in the form of IntelliSense support in your code editor. Simply type MySession., and IntelliSense will provide you with a drop-down list of all the available session variables. You'll no longer have to remember what they are, or what the string keys are that reference them.
  2. The properties are strongly typed. You'll no longer have to manually type-cast the session variables at the point of call. This facilitates the DRY principle: Don't Repeat Yourself. Centralize the type-cast in one place, and be done with it. Depending on the number of session variables you use, and how frequently you use them, this could provide a substantial cleanup of your code.
  3. The use of a class-level constant to refer to the session keys makes it vastly simpler to rename the session variable if you need to. You can simply change the constant's value, and even leave the property name alone if you need to. However, with modern refactoring tools like RefactorPro, this task is now far easier and safer because you're using a property on a class instead of a string literal.
  4. You can now document the session variables. Although the sample above only shows a summary description of each item for brevity's sake, the possibilities are endless. You can fully document each session variable, and know where to go to find that documentation in the code. A little clarity goes a long way.
  5. You can now deprecate a session variable by applying the ObsoleteAttribute to the property for the variable. Once you do that, the Task List pane will identify every point in the code that references that variable. If you want to refer the caller to a preferred session variable, you can do so with the string argument to the ObsoleteAttribute constructor.
  6. The Exists method allows you test for the existence of a valid session before querying it for information. This is tremendously helpful in avoiding an unexpected NullReferenceException in your code, particularly in exception handlers and error pages that happen to be .aspx pages.
  7. Because the session variables are wrapped in properties, you can write the getters to test for variable existence and return a default value if the variable is missing. This is a lot harder to do consistently if you are using string constants sprinkled throughout your code.

You might be wondering where the exception handling code is. There isn't any, and that's by design. If a session variable is missing in a property's getter, it doesn't do me any good to catch that condition and rethrow it--I won't be telling the caller anything that the exception won't already be telling them. My exception handling code in the pages and the fallback exception handler in global.asax are responsible for cleanly handling those exceptions.

It also doesn't do me any good to handle the NullReferenceException that will be thrown if I try to reference the Session and it's not there. Again, the global exception handler will take care of it. I could, of course, wrap the exception in a custom ApplicationException, and you always have that option. Then again, I could always perform the check by calling MySession.Exists before attempting to retrieve any properties, and avoid the exception altogether.

Points of Interest

For those familiar with the term, I eat my own dog food. I use this class, or one very similar to it, in my own applications, and in production code. Believe me, it saves time, and simplifies application maintenance and development. It reduces the intellectual complexity of the code enormously. Without a centralized "repository" for session variables, you never really know how many of them you have out there. This is especially true when multiple developers are working on the project: you never really know when someone has created a session variable on the fly, and no one else knows about it.

Of course, there's no guarantee that developers will use a class like this religiously once it's put in place. But a periodic search (using Find in Files) for "Session(" that turns up anything outside of the MySession object should send up red flags and alert you to the fact that something needs to be moved into it.

Encapsulating the details of the session object might not at first seem like it's a big deal. It's just a keyed collection, after all. It's when you start to think about the maintenance details that you realize what a nightmare it can become. When you start worrying about scalability, and realize that you need to pare down that session size, and you don't know where all those variables are, or what they're used for, that it starts to sink in. And when you get tired of casting the same variable to the same data type over and over again, you start writing functions to cast them for you--and you start wondering if someone else on the project hasn't already done so.

It's worth it to do it once, and do it right. A little upfront effort goes a long way, and reduces cost and risk down the road. So think about it, and if it fits your model, implement it. You might be glad you did. I certainly am.

History

  • Initial article -- March 2007
  • Added the missing Shared keyword to the Exists method -- 3/13/07

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Web Developer
United States United States
The Essential Geek is a lonely developer in a company lost somewhere in central Massachusetts. He writes ASP.NET and VB.NET applications targeting .NET 1.1 and .NET 2.0, using SQL Server 2000 and 2005.

Comments and Discussions

 
GeneralMy vote of 5 Pin
des199930-Nov-10 5:05
des199930-Nov-10 5:05 
QuestionConcurrency error Pin
JaimirG19-May-09 5:45
JaimirG19-May-09 5:45 
AnswerRe: Concurrency error [modified] Pin
des199929-Nov-10 2:10
des199929-Nov-10 2:10 
GeneralAdding "Remove" functionality Pin
otiose122-Dec-08 5:37
otiose122-Dec-08 5:37 
Questionpointless Exists function? Pin
elfoz1-Nov-08 4:30
elfoz1-Nov-08 4:30 
GeneralI want to use Public MySession on my dev computer, Friend MySession on production server Pin
John9703013-Mar-08 7:41
John9703013-Mar-08 7:41 
GeneralHaving problem converting to c# Pin
Palyam12-Nov-07 7:22
Palyam12-Nov-07 7:22 
GeneralCan't get it to work! Pin
hermiod10-Oct-07 3:32
hermiod10-Oct-07 3:32 
GeneralSame problem as others Pin
westicle16-Sep-07 0:34
westicle16-Sep-07 0:34 
QuestionProblem with shared property Pin
Savan Vadalia20-Aug-07 0:23
Savan Vadalia20-Aug-07 0:23 
GeneralProblem with Objects Pin
BobCuesta2-Aug-07 7:21
BobCuesta2-Aug-07 7:21 
QuestionGreat Idea, but I need some help, Pin
drl_at_bpd19-Jun-07 17:21
drl_at_bpd19-Jun-07 17:21 
AnswerRe: Great Idea, but I need some help, Pin
The Essential Geek26-Jun-07 3:26
The Essential Geek26-Jun-07 3:26 
GeneralError Pin
barretb13-Jun-07 2:02
barretb13-Jun-07 2:02 
GeneralRe: Error Pin
DougNichols20-Jun-07 3:32
DougNichols20-Jun-07 3:32 
GeneralRe: Error Pin
The Essential Geek26-Jun-07 3:27
The Essential Geek26-Jun-07 3:27 
GeneralRe: Error Pin
lauried26-Jun-07 10:05
lauried26-Jun-07 10:05 
GeneralRe: Error Pin
lucasparrish26-Jul-07 3:01
lucasparrish26-Jul-07 3:01 
GeneralNew question Pin
eddddy5-Jun-07 14:37
eddddy5-Jun-07 14:37 
I'm using something like this, but I added a shared (static) object to the class (to have all the user data across the web app)

It turns out that this object is shared with all user sessions, making a mess in the site.

Maybe putting the object in a session variable solve the problem, but I am worried about performance and privacy problems.


GeneralRe: New question Pin
The Essential Geek16-Jun-07 9:49
The Essential Geek16-Jun-07 9:49 
GeneralRe: New question Pin
peele7624-Jun-07 17:29
peele7624-Jun-07 17:29 
GeneralRe: New question Pin
The Essential Geek26-Jun-07 3:25
The Essential Geek26-Jun-07 3:25 
GeneralThis is Excellent Pin
Erasking24-May-07 7:13
Erasking24-May-07 7:13 
GeneralExcellent Pin
lstevensuk25-Apr-07 2:09
lstevensuk25-Apr-07 2:09 
QuestionHow to use? Pin
stretch19655-Apr-07 6:49
stretch19655-Apr-07 6:49 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.