WEBSITE

ASP.NET State Management : Customizing Session State Management

5/15/2013 1:47:08 AM
Since its beginning, the ASP.NET session state was devised to be an easy-to-customize and extensible feature. For various reasons, in ASP.NET 1.x it came out with a high degree of customization but a total lack of extensibility. In ASP.NET 2.0, the session state subsystem was refactored to allow developers to replace most of the functionalities—a characteristic that is often referred to as session state pluggability. In ASP.NET 3.5, you find the same set of capabilities as in the previous version.

All things considered, you have the following three options to customize session state management:

  • You can stay with the default session state module but write a custom state provider to change the storage medium (for example, to a non–SQL Server database or a different table layout). In doing so, you also have the chance to override some of the helper classes (mostly collections) that are used to bring data from the store to the Session object and back.

  • You can stay with the default session state module but replace the session ID generator. But hold on! The algorithm that generates session IDs is a critical element of the application, as making session IDs too easy for attackers to guess can lead straight to session-hijacking attacks. Nonetheless, this remains a customizable aspect of session state that, properly used, can make your application even more secure.

  • You can unplug the default session state module and roll your own. Technically possible also in ASP.NET 1.x, this option should be used as a last resort. Obviously, it provides the maximum flexibility, but it is also extremely complicated and is recommended only if strictly necessary and if you know exactly what you’re doing.

The first option—the easiest and least complicated of all—addresses most of the scenarios for which some custom session management is desirable. So let’s tackle it first.

Building a Custom Session State Provider

A session state provider is the component in charge of serving any data related to the current session. Invoked when the request needs to acquire state information, it retrieves data from a given storage medium and returns that to the module. Invoked by the module when the request ends, it writes the supplied data to the storage layer. As mentioned, ASP.NET supports three state providers, as listed in Table 1.

Table 1. Default State Providers
NameClassStorage Medium
InProcInProcSessionStateStoreStores data as live objects in the ASP.NET Cache.
StateServerOutOfProcSessionStateStoreStores data as serialized objects to the memory of a Windows service named aspnet_state.exe.
SQLServerSqlSessionStateStoreStores data as serialized objects into a SQL Server database.

In ASP.NET 2.0, you can write your own state provider class that uses the storage medium of your choice. Note that the default state providers also make use of various helper classes to move data around. In your custom provider, you can replace these classes too, or just stick to the standard ones.

Defining the Session State Store

A state provider (also often referred to as a session state store) is a class that inherits from SessionStateStoreProviderBase. The main methods of the interface are listed in Table 2.

Table 2. Methods of the SessionStateStoreProviderBase Class
MethodDescription
CreateNewStoreDataCreates an object to contain the data of a new session. It should return an object of type SessionStateStoreData.
CreateUninitializedItemCreates a new and uninitialized session in the data source. The method is called when an expired session is requested in a cookieless session state. In this case, the module has to generate a new session ID. The session item created by the method prevents the next request with the newly generated session ID from being mistaken for a request directed at an expired session.
DisposeReleases all resources (other than memory) used by the state provider.
EndRequestCalled by the default session state module when it begins to handle the EndRequest event.
GetItemReturns the session item matching the specified ID from the data store. The session item selected is locked for read. The method serves requests from applications that use a read-only session state.
GetItemExclusiveReturns the session item matching the specified ID from the data store and locks it for writing. Used for requests originated by applications that use a read-write session state.
InitializeInherited from the base provider class, performs one-off initialization.
InitializeRequestCalled by the default session state module when it begins to handle the AcquireRequestState event.
ReleaseItemExclusiveUnlocks a session item that was previously locked by a call to the GetItemExclusive method.
RemoveItemRemoves a session item from the data store. Called when a session ends or is abandoned.
ResetItemTimeoutResets the expiration time of a session item. Invoked when the application has session support disabled.
SetAndReleaseItemExclusiveWrites a session item to the data store.
SetItemExpireCallbackThe default module calls this method to notify the data store class that the caller has registered a Session_End handler.

Classes that inherit the SessionStateStoreProviderBase class work with the default ASP.NET session state module and replace only the part of it that handles session-state data storage and retrieval. Nothing else in the session functionality changes.

Locking and Expiration

Can two requests for the same session occur concurrently? You bet. Requests can arrive in parallel—for example, from two frames or when a user works with two instances of the same browser, the second of which is opened as a new window. To avoid problems, a state provider must implement a locking mechanism to serialize access to a session. The session state module determines whether the request requires read-only or read-write access to the session state and calls GetItem or GetItemExclusive accordingly. In the implementation of these methods, the provider’s author should create a reader/writer lock mechanism to allow multiple concurrent reads but prevent writing on locked sessions.

Another issue relates to letting the session state module know when a given session has expired. The session state module calls the method SetItemExpireCallback when there’s a Session_End handler defined in global.asax. Through the method, the state provider receives a callback function with the following prototype:

public delegate void SessionStateItemExpireCallback(
    string sessionID, SessionStateStoreData item);

It has to store that delegate internally and invoke it whenever the given session times out. Supporting expiration callbacks is optional and, in fact, only the InProc provider actually supports it. If your custom provider is not willing to support expiration callbacks, you should instruct the SetItemExpireCallback method to return false.

Note

A provider that intends to support cookieless sessions must also implement the CreateUninitialized method to write a blank session item to the data store. More precisely, a blank session item means an item that is complete in every way except that it contains no session data. In other words, the session item should contain the session ID, creation date, and perhaps lock IDs, but no data. ASP.NET 2.0 generates a new ID (in cookieless mode only) whenever a request is made for an expired session. The session state module generates the new session ID and redirects the browser. Without an uninitialized session item marked with a newly generated ID, the new request will again be recognized as a request for an expired session.


Replacing the Session Data Dictionary

SessionStateStoreData is the class that represents the session item—that is, a data structure that contains all the data that is relevant to the session. GetItem and GetItemExclusive, in fact, are defined to return an instance of this class. The class has three properties: Items, StaticObjects, and Timeout.

Items indicates the collection of name/values that will ultimately be passed to the page through the Session property. StaticObjects lists the static objects belonging to the session, such as objects declared in the global.asax file and scoped to the session. As the name suggests, Timeout indicates how long, in minutes, the session state item is valid. The default value is 20 minutes.

Once the session state module has acquired the session state for the request, it flushes the contents of the Items collection to a new instance of the HttpSessionStateContainer class. This object is then passed to the constructor of the HttpSessionState class and becomes the data container behind the familiar Session property.

The SessionStateStoreData class is used in the definition of the base state provider class, meaning that you can’t entirely replace it. If you don’t like it, you can inherit a new class from it, however. To both the session module and state provider, the container of the session items is merely a class that implements the ISessionStateItemCollection interface. The real class being used by default is SessionStateItemCollection. You can replace this class with your own as long as you implement the aforementioned interface.

Tip

To write a state provider, you might find helpful the methods of the SessionStateUtility class. The class contains methods to serialize and deserialize session items to and from the storage medium. Likewise, the class has methods to extract the dictionary of data for a session and add it to the HTTP context and the Session property.


Registering a Custom Session State Provider

To make a custom session state provider available to an application, you need to register it in the web.config file. Suppose you have called the provider class SampleSessionStateProvider and compiled it to MyLib. Here’s what you need to enter:

<system.web>
    <sessionState mode="Custom"
      customProvider="SampleSessionProvider">
      <providers>
        <add name="SampleSessionProvider"
          type="SampleSessionStateProvider, MyLib" />
      </providers>
    </sessionState>
</system.web>

The name of the provider is arbitrary but necessary. To force the session state module to find it, set the mode attribute to Custom.

Generating a Custom Session ID

To generate the session ID, ASP.NET 2.0 uses a special component named SessionIDManager. Technically speaking, the class is neither an HTTP module nor a provider. More simply, it is a class that inherits from System.Object and implements the ISessionIDManager interface. You can replace this component with a custom component as long as the component implements the same ISessionIDManager interface. To help you decide whether you really need a custom session ID generator, let’s review some facts about the default module.

The Default Behavior

The default session ID module generates a session ID as an array of bytes with a cryptographically strong random sequence of 15 values. The array is then encoded to a string of 24 URL-accepted characters, which is what the system will recognize as the session ID.

The session ID can be round-tripped to the client in either an HTTP cookie or a mangled URL, based on the value of the cookieless attribute in the <sessionState> configuration section. Note that when cookieless sessions are used, the session ID module is responsible for adding the ID to the URL and redirecting the browser. The default generator redirects the browser to a fake URL like the following one:

http://www.contoso.com/test/(S(session_id))/page.aspx

In ASP.NET 1.x, the fake URL is slightly different and doesn’t include the S(...) delimiters. How can a request for this fake URL be served correctly? In the case of a cookieless session, the Session ID module depends on a small and simple ISAPI filter (aspnet_filter.dll, which is also available to ASP.NET 1.x) to dynamically rewrite the real URL to access. The request is served correctly, but the path on the address bar doesn’t change. The detected session ID is placed in a request header named AspFilterSessionId.

A Homemade Session ID Manager

Now that we’ve ascertained that a session ID manager is a class that implements ISessionIDManager, you have two options: build a new class and implement the interface from the ground up, or inherit a new class from SessionIDManager and override a couple of virtual methods to apply some personalization. The first option offers maximum flexibility; the second is simpler and quicker to implement, and it addresses the most compelling reason you might have to build a custom session ID generator—to supply your own session ID values.

Let’s start by reviewing the methods of the ISessionIDManager interface, which are shown in Table 3.

Table 3. Methods of the ISessionIDManager Interface
MethodDescription
CreateSessionIDVirtual method. It creates a unique session identifier for the session.
DecodeDecodes the session ID using HttpUtility.UrlDecode.
EncodeEncodes the session ID using HttpUtility.UrlEncode.
InitializeInvoked by the session state immediately after instantiation; performs one-time initialization of the component.
InitializeRequestInvoked by the session state when the session state is being acquired for the request.
GetSessionIDGets the session ID from the current HTTP request.
RemoveSessionIDDeletes the session ID from the cookie or from the URL.
SaveSessionIDSaves a newly created session ID to the HTTP response.
ValidateConfirms that the session ID is valid.

If you plan to roll your own completely custom session ID generator, bear in mind the following points:

  • The algorithm you choose for ID generation is a critical point. If you don’t implement strong cryptographic randomness, a malicious user can guess a valid session ID when the same session is still active, thus accessing some user’s data. (This is known as session hijacking.) A good example of a custom session ID algorithm is one that returns a globally unique identifier (GUID).

  • You can choose to support cookieless sessions or not. If you do, you have to endow the component with the ability to extract the session ID from the HTTP request and redirect the browser. You probably need an ISAPI filter or HTTP module to preprocess the request and enter appropriate changes. The algorithm you use to store session IDs without cookies is up to you.

If you are absolutely determined to have the system use your session IDs, you derive a new class from SessionIDManager and override two methods: CreateSessionID and Validate. The former returns a string that contains the session ID. The latter validates a given session ID to ensure it conforms to the specification you set. Once you have created a custom session ID module, you register it in the configuration file. Here’s how to do it:

<sessionState
    sessionIDManagerType="Samples.MyIDManager, MyLib" />
</sessionState>

Session State Performance Best Practices

State management is a necessary evil. By enabling it, you charge your application with an extra burden. The September 2005 issue of MSDN Magazine contains an article with the ASP.NET team’s coding best practices to reduce the performance impact of session state on Web applications.

The first guideline is disabling session state whenever possible. However, to prevent the session from expiring, the HTTP module still marks the session as active in the data store. For out-of-process state servers, this means that a roundtrip is made. Using a custom session ID manager returning a null session ID for requests that are known not to require session state is the best way to work around this issue and avoid the overhead entirely. (Write a class that inherits from SessionIDManager and overrides GetSessionID.)

The second guideline entails minimizing contention on session data by avoiding frames and downloadable resources served by session-enabled handlers.

The third guideline relates to data serialization and deserialization. You should always use simple types and break complex classes into arrays of simple properties, at least as far as session management is concerned. In other words, I’m not suggesting that you should factor out your DAL classes—just change the way you serialize them into the session store. An alternate approach entails building a custom serialization algorithm that is optimized for session state storage. Breaking a class into various properties, with each stored in a session slot, is advantageous because of the simple types being used, but also because the extreme granularity of the solution minimizes the data to save in case of changes. If one property changes, only one slot with a simple type is updated instead of a single slot with a complex type.

Other  
 
Top 10
Review : Sigma 24mm f/1.4 DG HSM Art
Review : Canon EF11-24mm f/4L USM
Review : Creative Sound Blaster Roar 2
Review : Philips Fidelio M2L
Review : Alienware 17 - Dell's Alienware laptops
Review Smartwatch : Wellograph
Review : Xiaomi Redmi 2
Extending LINQ to Objects : Writing a Single Element Operator (part 2) - Building the RandomElement Operator
Extending LINQ to Objects : Writing a Single Element Operator (part 1) - Building Our Own Last Operator
3 Tips for Maintaining Your Cell Phone Battery (part 2) - Discharge Smart, Use Smart
REVIEW
- First look: Apple Watch

- 3 Tips for Maintaining Your Cell Phone Battery (part 1)

- 3 Tips for Maintaining Your Cell Phone Battery (part 2)
VIDEO TUTORIAL
- How to create your first Swimlane Diagram or Cross-Functional Flowchart Diagram by using Microsoft Visio 2010 (Part 1)

- How to create your first Swimlane Diagram or Cross-Functional Flowchart Diagram by using Microsoft Visio 2010 (Part 2)

- How to create your first Swimlane Diagram or Cross-Functional Flowchart Diagram by using Microsoft Visio 2010 (Part 3)
Popular Tags
Microsoft Access Microsoft Excel Microsoft OneNote Microsoft PowerPoint Microsoft Project Microsoft Visio Microsoft Word Active Directory Biztalk Exchange Server Microsoft LynC Server Microsoft Dynamic Sharepoint Sql Server Windows Server 2008 Windows Server 2012 Windows 7 Windows 8 Adobe Indesign Adobe Flash Professional Dreamweaver Adobe Illustrator Adobe After Effects Adobe Photoshop Adobe Fireworks Adobe Flash Catalyst Corel Painter X CorelDRAW X5 CorelDraw 10 QuarkXPress 8 windows Phone 7 windows Phone 8