The problem of session state is a significant one for Web projects on numerous platforms. Microsoft’s Windows DNA platform is notorious for its poor handling of session values. Scalability problems abound, as session state cannot be handled from machine to machine and calls to recover session values incur massive overhead. Here’s how I handled sessions in the development of Unbound, a turn-based strategic simulation game played over the Internet.

One solution for most developers is to build a simple session-state manager from scratch as part of the project code. This way, developers can track only what is needed, a strategy that will improve performance. In Unbound, I need only to track the authenticated user. I will look up the rest as needed.

I created a state-management strategy that:

  • ·        Creates a record with a global identifier every time a user logs on.
  • ·        Updates the record every time a user calls a page.
  • ·        Allows every page the use of the validated userID.
  • ·        Erases the record after a period of time.

Keys to the state
The key to good state management on the Web is the proper division of labor between the SQL Server, COM+, and IIS. The layers involved include:

Data access layer
SQL Server will serve its normal role as a database management system. We can schedule a SQL Job to remove rows as they expire, using the Delete stored procedure. The simple table looks like this:
tblState { userKey (GUID), userID(int), updateTime(datetime) }

We’ll build four stored procedures that will be abstracted by four methods in our COM+ object. These procedures will allow us to AddNew state rows, Update them, Delete them, and GetByKey. The userKey field is the table identity and creates its own GUID automatically, so in general, the AddNew, Update, and Delete stored procedures are fairly standard. The GetByKey stored procedure should accept a GUID and return a recordset containing a single row of the state table.

Business logic layer
The COM+ component is acting almost as a data layer component in this scenario. In this way, we avoid the mistake that I believe Microsoft made—having the component layer actually do too much work. Our single clsState class will do nothing more than encapsulate the four stored procedures we discussed above. The majority of the business logic will be accomplished in the ASP code that we’ll include on every page, with one exception. Since we aren’t having SQL Server remove expired sessions, we’ll have the component take care of it.

On the GetByKey method, the component will call its related stored procedure and receive a recordset. If the recordcount is zero, the user should be redirected to the login page, handled by IIS. If the record comes back and the date is older than an hour, the record should be deleted with the Delete method and the user should be treated as if there was never a session.

Presentation layer
The IIS layer does most of the work in this example. In fact, our header code that we included on every secure page will sponsor three of the four original goals of our application. 

First, at the top of each page, we’ll call the GetByKey method. This method calls the GetByKey stored procedure, using the GUID stored in a cookie by a previously accessed page. If there is no GUID, or the recordset comes back with no records, this means that the user hasn’t been authenticated. The user must be directed to the login page to get a new GUID.

Second, we need to store the GUID in a cookie with a common name, overwriting any previous record, and store the userID in a page level variable. This is in case users got a new GUID by logging in again and we need to erase a previous record. The userID will only be good for the life of the page. We won’t store the userID in the HTML file, only the server script, so that the user doesn’t have access to the userID.

Finally, we need to update the user’s row in the state table by calling the Update method in the COM component. If the user is authenticated, meaning a recordset with one record was returned, we pass in the GUID and the userID with the current datetime. This will prevent the SQL Job from deleting the record on its regular delete pass.

The pseudocode for the file included on every secure page is shown in Listing A.

Finishing up
Though Unbound is an application using Windows DNA, the concepts presented here are usable with any architecture, which is one of the reasons for presenting the pseudocode. Although the .Net Framework includes a fairly solid state-management system, the concept presented here will work just as well. PHP with COM, or JSP with EJB, could also use a system like this.

The next likely step for most programmers will be to set up a system to use the userID on all pages. If you want to customize each page, simply look up the name from the user table using the userID. We could also check the role of the user in the same table and assure the user has permission to view the requested page, or you could customize the menu. There are numerous potential uses for this.