Implementing a Session-State Store Provider
Describes a custom session-state store provider implementation and demonstrates implementing a sample provider.
ASP.NET session state is designed to enable you to store user session data in different sources. By default, session state values and information are stored in memory within the ASP.NET process. One alternative is to store session data in a state server, which keeps session data in a separate process and retains it if the ASP.NET application is shut down and restarted. Another alternative is to store session data in a SQL Server database, where it can be shared by multiple Web servers.
You can use the session-state stores that are included with ASP.NET, or you can implement your own session-state store provider. You might create a custom session-state store provider for the following reasons:
You need to store session-state information in a data source other than SQL Server, such as a FoxPro database or an Oracle database.
You need to manage session-state information using a database schema that is different from the database schema used by the providers that ship with the .NET Framework. An example of this would be shopping cart data that is stored with a predefined schema in your existing SQL Server database.
You can implement a custom session-state store provider by creating a class that inherits the SessionStateStoreProviderBase class. For more information, see the "Required Classes" section later in this topic.
Session state is managed by the SessionStateModule class, which calls the session-state store provider to read and write session data to the data store at different times during a request. At the beginning of a request, the SessionStateModule instance retrieves data from the data source by calling the GetItemExclusive method, or if the EnableSessionState page attribute has been set to ReadOnly, by calling the GetItem method. At the end of a request, if the session-state values have been modified, the SessionStateModule instance calls the SessionStateStoreProviderBase.SetAndReleaseItemExclusive method to write the updated values to the session-state store. SessionStateModule calls additional members of the SessionStateStoreProviderBase implementation to initialize a new session as well as to delete session data from the data store when the HttpSessionState.Abandon method is called. Each member of the SessionStateStoreProviderBase class is discussed in more detail in the "Required Classes" section later in this topic.
The SessionStateModule class determines the SessionID value itself, rather than relying on the session-state store provider to do so. If needed, you can implement a custom SessionIDManager by creating a class that inherits the ISessionIDManager interface. For more information, see the "Remarks" section in ISessionIDManager.
SessionStateModule will revert to the ASP.NET process identity to access any secured resource, such as a database server. You can specify that the SessionStateModule instance impersonate the identity supplied by IIS by setting the useHostingIdentity attribute of the <sessionState> configuration element to false. For example, if you have configured your IIS application to use Windows Integrated security and you want ASP.NET to impersonate the identity provided by IIS for session management, specify <identity impersonate="true" /> in the <system.web> configuration section of the Web.config file for the application, and set the useHostingIdentity attribute of the <sessionState> configuration element to false. If the useHostingIdentity attribute is true, ASP.NET will impersonate the process identity, or the user credentials supplied to the <identity> configuration element (if they exist) when connecting to the data source. For more information on the ASP.NET process identity, see Configuring ASP.NET Process Identity and ASP.NET Impersonation.
ASP.NET applications are multithreaded so they can respond to multiple concurrent requests. Multiple concurrent requests might attempt to access the same session information. Consider a scenario where multiple frames in a frameset all reference ASP.NET Web pages in the same application. The separate requests for each frame in the frameset might be executed on the Web server concurrently on different threads. If the ASP.NET pages for each frame access session-state variables, you could have multiple threads accessing the session store concurrently. To avoid data collisions at the session store and unexpected session-state behavior, the SessionStateModule and SessionStateStoreProviderBase classes include functionality that exclusively locks the session-store item for a particular session during the execution of an ASP.NET page. Note that no lock is set on a session-store item if the EnableSessionState attribute is marked as ReadOnly. However, other ASP.NET pages in the same application might be able to write to the session store, so a request for read-only session data from the store might still have to wait for locked data to be freed.
A lock is set on session-store data at the beginning of the request in the call to the GetItemExclusive method. When the request completes, the lock is released during the call to the SetAndReleaseItemExclusive method.
If the SessionStateModule instance encounters locked session data during the call to either the GetItemExclusive or GetItem method, it will re-request the session data at half-second intervals until either the lock is released or the amount of time specified in the ExecutionTimeout property has elapsed. If the request times out, SessionStateModule calls the ReleaseItemExclusive method to free the session-store data and request the session-store data at that time.
Locked session-store data might have been freed by a call to the ReleaseItemExclusive method on a separate thread, before the call to the SetAndReleaseItemExclusive method for the current response. This could cause the SessionStateModule instance to set and release session-state store data that has already been released and modified by another session. To avoid this situation, SessionStateModule includes a lock identifier with each request to modify locked session-store data. Session-store data is only modified if the lock identifier in the data store matches the lock identifier supplied by SessionStateModule.
When the Abandon method is called for a session, the data for that session is deleted from the data store using the RemoveItem method. Otherwise, the data remains in the session data store to serve future requests for the session.
The mechanism for deleting expired session data depends on the capabilities of your data source. If your data source can be configured to delete expired session data according to the session Timeout property, you can use the SetItemExpireCallback method to reference the delegate for the Session_OnEnd event and raise it when deleting expired session data.
To maintain session scope, session-state providers store session information uniquely for each application. This allows multiple ASP.NET applications to use the same data source without running into a conflict if duplicate session identifiers are encountered.
Because session-state store providers store session information uniquely for each application, you must ensure that your data schema, queries, and updates include the application name. For example, the following command might be used to retrieve session data from a database.
SELECT * FROM Sessions WHERE SessionID = 'ABC123' AND ApplicationName = 'MyApplication'
Alternatively, you can store a combination of the session identifier and the application name as the unique identifier for an item in the session-state data store.
To implement a session-state store provider, create a class that inherits the SessionStateStoreProviderBase abstract class. The SessionStateStoreProviderBase class in turn inherits the ProviderBase abstract class, so you must implement the required members of the ProviderBase class as well. The following tables list the properties and methods that you must implement from the ProviderBase and SessionStateStoreProviderBase abstract classes and provides a description of each. To view an implementation of each member, see Sample Session-State Store Provider.
Required ProviderBase Members
Takes as input the name of the provider and a NameValueCollection instance of configuration settings. This method is used to set property values for the provider instance, including implementation-specific values and options specified in the configuration file (Machine.config or Web.config).
Required SessionStateStoreProvider Members
Takes as input the HttpContext instance for the current request and performs any initialization required by your session-state store provider.
Takes as input the HttpContext instance for the current request and performs any cleanup required by your session-state store provider.
Frees any resources no longer in use by the session-state store provider.
Takes as input the HttpContext instance for the current request and the SessionID value for the current request. Retrieves session values and information from the session data store and locks the session-item data at the data store for the duration of the request. The GetItemExclusive method sets several output-parameter values that inform the calling SessionStateModule about the state of the current session-state item in the data store.
If no session item data is found at the data store, the GetItemExclusive method sets the locked output parameter to false and returns null. This causes SessionStateModule to call the CreateNewStoreData method to create a new SessionStateStoreData object for the request.
If session-item data is found at the data store but the data is locked, the GetItemExclusive method sets the locked output parameter to true, sets the lockAge output parameter to the current date and time minus the date and time when the item was locked, sets the lockId output parameter to the lock identifier retrieved from the data store, and returns null. This causes SessionStateModule to call the GetItemExclusive method again after a half-second interval, to attempt to retrieve the session-item information and obtain a lock on the data. If the value that the lockAge output parameter is set to exceeds the ExecutionTimeout value, SessionStateModule calls the ReleaseItemExclusive method to clear the lock on the session-item data and then call the GetItemExclusive method again.
The actionFlags parameter is used with sessions whose Cookieless property is true, when the regenerateExpiredSessionId attribute is set to true. An actionFlags value set to InitializeItem (1) indicates that the entry in the session data store is a new session that requires initialization. Uninitialized entries in the session data store are created by a call to the CreateUninitializedItem method. If the item from the session data store is already initialized, the actionFlags parameter is set to zero.
If your provider supports cookieless sessions, set the actionFlags output parameter to the value returned from the session data store for the current item. If the actionFlags parameter value for the requested session-store item equals the InitializeItem enumeration value (1), the GetItemExclusive method should set the value in the data store to zero after setting the actionFlags out parameter.
This method performs the same work as the GetItemExclusive method, except that it does not attempt to lock the session item in the data store. The GetItem method is called when the EnableSessionState attribute is set to ReadOnly.
Takes as input the HttpContext instance for the current request, the SessionID value for the current request, a SessionStateStoreData object that contains the current session values to be stored, the lock identifier for the current request, and a value that indicates whether the data to be stored is for a new session or an existing session.
If the newItem parameter is true, the SetAndReleaseItemExclusive method inserts a new item into the data store with the supplied values. Otherwise, the existing item in the data store is updated with the supplied values, and any lock on the data is released. Note that only session data for the current application that matches the supplied SessionID value and lock identifier values is updated.
Takes as input the HttpContext instance for the current request, the SessionID value for the current request, and the lock identifier for the current request, and releases the lock on an item in the session data store. This method is called when the GetItem or GetItemExclusive method is called and the data store specifies that the requested item is locked, but the lock age has exceeded the ExecutionTimeout value. The lock is cleared by this method, freeing the item for use by other requests.
Takes as input the HttpContext instance for the current request, the SessionID value for the current request, and the lock identifier for the current request, and deletes the session information from the data store where the data store item matches the supplied SessionID value, the current application, and the supplied lock identifier. This method is called when the Abandon method is called.
Takes as input the HttpContext instance for the current request, the SessionID value for the current request, and the lock identifier for the current request, and adds an uninitialized item to the session data store with an actionFlags value of InitializeItem.
The CreateUninitializedItem method is used with cookieless sessions when the regenerateExpiredSessionId attribute is set to true, which causes SessionStateModule to generate a new SessionID value when an expired session ID is encountered.
The process of generating a new SessionID value requires the browser to be redirected to a URL that contains the newly generated session ID. The CreateUninitializedItem method is called during an initial request that contains an expired session ID. After SessionStateModule acquires a new SessionID value to replace the expired session ID, it calls the CreateUninitializedItem method to add an uninitialized entry to the session-state data store. The browser is then redirected to the URL containing the newly generated SessionID value. The existence of the uninitialized entry in the session data store ensures that the redirected request with the newly generated SessionID value is not mistaken for a request for an expired session, and instead is treated as a new session.
The uninitialized entry in the session data store is associated with the newly generated SessionID value and contains only default values, including an expiration date and time, and a value that corresponds to the actionFlags parameter of the GetItem and GetItemExclusive methods. The uninitialized entry in the session state store should include an actionFlags value equal to the InitializeItem enumeration value (1). This value is passed to SessionStateModule by the GetItem and GetItemExclusive methods and specifies for SessionStateModule that the current session is a new session. SessionStateModule will then initialize the new session and raise the Session_OnStart event.
Takes as input the HttpContext instance for the current request and the Timeout value for the current session, and returns a new SessionStateStoreData object with an empty ISessionStateItemCollection object, an HttpStaticObjectsCollection collection, and the specified Timeout value. The HttpStaticObjectsCollection instance for the ASP.NET application can be retrieved using the GetSessionStaticObjects method.
Takes as input a delegate that references the Session_OnEnd event defined in the Global.asax file. If the session-state store provider supports the Session_OnEnd event, a local reference to the SessionStateItemExpireCallback parameter is set and the method returns true; otherwise, the method returns false.
To view an example implementation of a custom session-state store provider that manages session information in an Access database, see Sample Session-State Store Provider.