Introduction
This article describes an easy way to improve a WebApplication that uses the Session class directly.
Implementing these kind of improvements is called "Refactoring".
Because it is specifically targeted to WebApplications I call it "WebRefactoring".
First, I will give a brief explaination about what Refactoring is about, and what I mean with "WebRefactoring".
Secondly, I will give a refactoring solution for WebApplications that use the Session object directly.
Background
"Refactoring is the process of improving the design of existing code" in a low-risk, step-by-step, easy manageable way.
Refactoring is about solving common design problems that are the result of:
- Duplicated code;
- Unclear code;
- Complicated code.
Thus, WebRefactoring is the process of improving existing WebApplications by removing duplicated, unclear and complicated code.
Because it is about improving existing code I will give an example of a possible "before" and "after" situation.
Before Refactoring
I think that Session values can be thought of as Global Variables. If you use many of these global's directly you are going against one of the basic principles of Software Engineering: Encapsulation and Abstraction. The disadvantages of lacking encapsulation are obvious: everybody using these - none encapsulated - variables needs to have full understanding of all possible values and states. In most cases it is just a matter of time before unexpected errors occur. When this happens you will notice that because the scope is global your problem domain is global and you have to study the whole application.
Code like this usually (should) looks like this:
...
Int32 nUserID = -1;
if ( null != Session["userID"] ) {
if ( Session["userID"] is Int32 ) {
if ( 0 < Session["userID"] ) {
nUserID = (Int32) Session["userID"]
}
}
}
if ( -1 == nUserID )
{
throw new ApplicationException ( "Unexpected situation: userID invalid." );
}
this.doSomething( nUserID )
...
As you can imagine this is an excellent candidate for refactoring because it is:
- Duplicate code: everywhere you want to use this global you duplicate the checking before using;
- Unclear: this code makes the code unclear because it includes many lines to "just" use the userid making it difficult to see what the main code is really about;
- Complicated: to just use userID you need to know the type, the default value and the session-key and test if it exists at all.
After refactoring
When you improve Session related code by encapsulating the logic in a specialized class thus implement the solution described the quality of code dramatically improves:
...
this.doSomething( CCurrentSession.UserID )
...
Now, anybody can see the advantage of applying the change:
- Duplicate code: removed;
- unclear: Nothing unclear about the code ( except for the doSomething example function I guess :-)
- Complicated: No kowledge needed to get the session-UserID
Actual Refactoring Process
Sofar, we have seen the "before" and "after" situation. But what does actually do we need to do to implement this improvement in a easy managable, low risk, step-by-step way?
Roadmap:
The roadmap to actually implement this improvement involves three steps:
- Create the CCurrentSession - class ( I have included an example that I will discuss next );
- Implement your first (static) property: to make a smooth transition don't move all your session objects to the wrapper class at one but move them one-by-one. I would start with the ones that have the least logic and are the most easily to test. This way you can get acquainted to in a relaxed way.
- Replace the code that uses the session object directly to use the CCurrentSession class instead. You should be able to do this by using search/replace.
The CCurrentSession - class
For the initial version of the CCurrentSession - class I have defined the following type of properties:
- Properties that have a default value if no value has previously been set;
- Properties that can only be used if a value has previously has been set;
- Properties that can have the value Null assigned;
- Properties that cannot have the value Null assigned;
These types have been implemented in four different private helper functions:
- private: GetValueOrDefault: get the value for properties that have a default value;
- private: GetMandatoryValue: get the value for properties that must have a previously set value to use;
- private: SetMandatoryValue: set the value for properties cannot be Null;
- private: GetValue: get a session object from a specified key from the session object;
- private: SetValue: set an object for a specific key to the session object;
Accessing the Sessionstate:
I expose the properties of the CCurrentSession class as static properties so we don't have to instantiate the object. This is really easy because to get access to the Sessionstate-object I use the static "HttpContext.Current" property which returns the HttpContext object for the current HTTP request.
Finally, I have used an enum for storing the key values of the different session Items. This way we do not have to worry about typing mistakes.
Aditional advantages:
- The ability to implement "lazy loading" of properties. Imagine that your cCurrentSession - class evolves to contain more logic that involved combining values as a result of a calculation or data-retrievel.
- Documentation: by using the CCurrentSession-class you can centralize the comments and documetation text for the session properties. Before, there was no central way to do this. Now for example, by adding xml-comments to the properties and the CCurrentSession class your comments get pickeped by a documentation tool like xDoc.
- Intellicense : by using the CCurrentSession-class we now get Intellicense support. If we have choses the propertynames carefully we can discover the properties as we work. This was not possible before.
- Compiler help: because properties can return the actual native-objects the compiler helps us by checking if the types match. For example, when we want to use the UserID property as a date the compiler will complain. This was not the situation before.
- Tracing: Adding trace information when session-values change has become really simple. Before, if a session-value is changed in many different places this would be anything but simple.
Points of Interest
For more information about refactoring you can visit: http://www.refactoring.com/
Recently, I have started describing my WebApplication development experiences on my website: http://www.vaneijkel.com/
Conclusion
Improving an existing web application can be a big challenge, however if you do it right it can really easy and fun.
Please share your SessionWrapper refactoring experiences with me.
About Marcel van Eijkel
Marcel van Eijkel has been a professional Microsoft Web Application developer since 1998. Since then, he has been involved in many Web Application rebuilding projects.
He has is a big interest in Architecture, Design Patterns, Refactoring.
His favorite technologies are DotNet and SQL Server.
At this moment he is gathering his Microsoft Certificates one by one and finishing his bachelor degree information technology.
You can find my website at: http://www.vaneijkel.com/
History
29 Aug 2005: initial version.