Introduction

The Session_End event is a useful event which an be handled in Global.asax to perform any actions when a session ends, such as logging an activity to the database, cleaning up temporary session files, etc.

However, when using any kind of state management other than InProc (such as StateServer or SqlStateServer), the ASP.NET web application does not fire the Session_End event, and any code in this method will not be executed.

Background

Some browsing around returned a couple of good articles. The article Page tracking in ASP.NET offers a similar solution, though is geared around page tracking whereas my requirement was simply to find an alternative to the Session_End event that would work with the ASP.NET StateServer.

There's another excellent article called Preventing Multiple Logins in ASP.NET This is where I got the idea of using the application cache with a sliding expiry to trigger an event when the session ends.

How it works

The SessionEndModule class hooks into the PreRequestHandlerExecute event and inserts/replaces an item in the application cache, with a sliding expiry equal to the session expiry, and a callback method to be called when the item is removed from the application cache. The key of the cache item is the SessionId, and the value is the value of the item in the session with the key set as the SessionObjectKey.

When the item expires and the callback method is called. The key, value and reason of the expired item is passed to this callback method. The key is the SessionId, the value is the value copied from the session, and the reason is why the item was removed from the cache (was it removed, expired, underused, or it's dependency changed).

The callback method then checks that the item was removed as a result of it expiring, wraps the values into a SessionEndEventArgs class (which exposes SessionId and SessionObject properties), and fires the SessionEnd event.

Using the code

The code consists of a HttpModule called SessionEndModule, which needs to be included in the project via the web.config file. It exposes a static property named SessionObjectKey and a static event named SessionEnd which will be fired when a session ends. The value of the SessionObjectKey will be returned in the event arguments for the SessionEnd event.

First lets set up web.config

<httpModules><addname="SessionEndModule"type="SessionTestWebApp.Components.SessionEndModule, SessionTestWebApp"/></httpModules><!--<span class="code-comment"> Use the state server (rather than InProc), and set the timeout to 1 minute for easier testing--></span><sessionStatemode="StateServer"stateConnectionString="tcpip=127.0.0.1:42424"timeout="1"cookieless="false"/>

Testing the code

Start this project in debug mode and keep an eye on the output window. When the session starts, the session contents will populated with a random UserId and UserEmail. The session will end after approximately 1 minute, and fire the SessionEnd event, which will execute the SessionTimoutModule_SessionEnd method and print the SessionId of the session that ended and the UserEmail it contained.

Returning more than one session value

The HttpModule.SessionEnd event only supports returning the value of a single session variable. If you need to return more than one value, the easiest way is to create a serializable class, with properties for all your values, and store that in the session instead.

Known limitations

As the module hooks into the PreRequestHandler event, the value of the SessionObjectKey stored in the application cache before the page executes. This means that if you change the session variable being returned by the module in the SessionEnd event (or the SessionInfo object) on a page, and then the session times out, the SessionEnd event will contain the old value. It's possible to change this to use the PostRequestHandler event, though I've experienced some strange behaviour with this, especially when using Response.TransmitFile.

This code also won't work on scenerarios where you are using a server farm. In the article 'Preventing Multiple Logins in ASP.NET', Peter Bromberg discusses this problem in more depth and offers some ideas for working around this.

Hi,I've a problem with deploying to server. I've made everything according to sample, when I start it from Visual Studio it works perfectly. But when I publish project to IIS 7.5, the SessionTimoutModule_SessionEnd doesn't fire.

I have a query regarding redirection to Login page, which I am not being able to trace out.

When I click on any of the tab in my application (which internally loads a new aspx page) sometimes itis redirected to Login.aspx, which is unexpected. Couldnot debug why this is happening even by javascript alerts and C# debugging statements.

Below is the view source piece from Login.aspx, just for clue<form name="Form1" method="post" action="login.aspx?ReturnUrl=%2fValuations%2fToDoList.aspx" id="Form1">

Valuations is the dll of application and ToDoList.aspx is the tab page I clicked which should load, but suddenly Login.aspx page is displayed. I traced the complete solution project.

- in web.config<authorization><deny users="?"/>

</authorization>

- -suddenly while traversing in the application it redirects to login.aspx including return url as stated earlier and also if we keep the application idle for 5-10 mins and clicks somewhere, still redirects....

- - we have also AjaxPro.dll for async calls.

- when the login.aspx page is not closed then, below logger statements are seen after 5 mins-public Global(){_log4netLogger.Debug("Global:Global");InitializeComponent();}- catch of Session_End (because System.Web.HttpContext.Current.Application["userPool"]; is null) - Application_End

Thanks for this nice article. This code is working when session timeout. But what about when we call session.abandon() ?In that case SessionTimoutModule_SessionEnd not fired.

I want to clear some temp file on session end event in state server session mode.In session timeout case this is working. bu t when user logout using FormsAuthentication.SignOut() Session.Abandon()methods SessionTimoutModule_SessionEnd not fired.

Thanks for the feedback. I've updated the code sample so that it's now using the website project model instead of the web application project model, which should solve this issue. Let me know if you have any further problems.

Thanks for the feedback. This is due to the way ASP.NET manages the application cache. I've updated the article accordingly and mentioned this as a known limitation, with a link to the 'Preventing Multiple Logins in ASP.NET article by Peter Bromberg where he offers some ideas for working around this.