Visual Studio project structure overview

Session expiration in ASP.NET is a nasty problem. Imagine yourself filling a long form, hitting submit then boom you get the login page! That happened to me before and probably happened to you.

This problem doesn’t have an out-of-box solution in ASP.NET and there are different appraoches to solve it.

  1. Increasing the session time out. Probably you are aware of the cons of this problem which are mainly consuming more server resources.
  2. Having an image or an iframe that refreshes regularly and requests the server. This approach is a per page approach and you can select the pages to implement it on.

Personally, I’ve been using the second approach for a long time and didn’t have any problem with it. However, I made it more reusable, not to mention “cleaner”, by making it a web control.

If you are just interested in using the control then you can skip to the last section “Using the Control.”

Architecture

The architecture here is simple and consists of both server-side and client-side solutions.

Server-Side

An ASHX handler file hosted on the root of the site which when requested will renew the session and return a 1-pixel image. In my control, I called the ASXH file as session-keep-alive.ashx and this is the code:

<%@WebHandler Language="C#" Class="session_keep_alive">
using System.Web;
using System.Web.SessionState;

public class session_keep_alive : 
  IHttpHandler, IRequiresSessionState 
{
  private static byte[] gif = 
    {
      0x47,0x49,0x46,0x38,0x39,0x61,0x01,0x00,0x01,0x00,0x91,
      0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
      0x00,0x00,0x00,0x21,0xf9,0x04,0x09,0x00,0x00,0x00,0x00,
      0x2c,0x00,0x00,0x00,0x00,0x01,0x00,0x01,0x00,0x00,0x08,
      0x04,0x00,0x01,0x04,0x04,0x00,0x3b,0x00
    };

  public void ProcessRequest (HttpContext context)
  {
    context.Response.ContentType = "image/gif";
    context.Response.Cache.SetCacheability(
                                HttpCacheability.NoCache);
    context.Response.BinaryWrite(gif);
    context.Response.End();
  }

  public bool IsReusable {
    get 
    {
      return true;
    }
  }

}

Client-Side

On the client, my web control will render a 1-pixel image and some JavaScript code that will refresh the image every set amount of time from the ASXH file on the server. This is the control’s code:

using System;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.ComponentModel;

namespace AT.Web.UI.Controls 
{

/// <summary>
/// Web control to keep the session alive by refreshing 
/// every set amount of time
/// </summary>
[AspNetHostingPermission(SecurityAction.InheritanceDemand, 
    Level = AspNetHostingPermissionLevel.Minimal)]
[AspNetHostingPermission(SecurityAction.LinkDemand, 
    Level = AspNetHostingPermissionLevel.Minimal)]
[ToolboxData("<{0}:SessionKeepAlive runat=server />")]
public class SessionKeepAlive : WebControl {

/// <summary>
/// Set, in minutes, the duration to trigger the session.
/// The default value is one minute
/// </summary>
[Category("Behavior")]
[DefaultValue(1)]
public int Duration {
  get 
  {
    return (int)(ViewState["Duration"] ??  1);
  }
  set 
  {
    ViewState["Duration"] = value;
  }
}

/// <summary>
/// The path of the image's URL that will be requested from the 
/// server, the default is ~/session-keep-alive.ashx
/// </summary>
[Category("Behavior")]
[DefaultValue("~/session-keep-alive.ashx")]
public string PageUrl 
{
  get 
  {
    return (string)(ViewState["PageUrl"] ?? "~/session-keep-alive.ashx");
  }
  set 
  {
    ViewState["PageUrl"] = value;
  }
}

/// <summary>
/// Applies in-line CSS style to hide the generated image. 
/// The default is true. Alternatively, you can set this to false 
/// and hide the image using your own CSS to keep your page 
/// XHTML Strict compatible.
/// </summary>
[Category("Behavior")]
[DefaultValue(true)]
public bool AutoHide
{
  get 
  {
    return (bool)(ViewState["AutoHide"] ?? true);
  }
  set 
  {
    ViewState["AutoHide"] = value;
  }
}

protected override void OnPreRender(EventArgs e) {
  base.OnPreRender(e);

  if (AutoHide) 
  {
    this.Style.Add(HtmlTextWriterStyle.Display, "none");
  }

  Page.ClientScript.RegisterClientScriptResource(
    typeof(SessionKeepAlive),
    "AT.Web.UI.Controls.JavaScript.js");

  Page.ClientScript.RegisterStartupScript(
    typeof(SessionKeepAlive), "SessionKeepAlive",

  string.Format(
    @"window.setInterval('SessionKeepAlive(\'{0}\', \'{1}\');', {2});",
    ClientID, ResolveClientUrl(PageUrl), Duration * 60000), true);
}

protected override void Render(HtmlTextWriter writer) 
{
  writer.AddAttribute(
    HtmlTextWriterAttribute.Src, ResolveClientUrl(PageUrl));
    writer.AddAttribute(HtmlTextWriterAttribute.Alt, " ");
    base.RenderBeginTag(writer);
    base.RenderEndTag(writer);
}

}
}

And here is the used JavaScript, note that I am setting the Src of the image to
the same location (the ASHX file), but changing the query string to prevent the
browser from caching the request.

function SessionKeepAlive(imageID, pageUrl) 
{
  var imageObj = document.getElementById(imageID);
  if (imageObj == null || imageObj == undefined) 
  {
    return;
  }
  imageObj.src = pageUrl + "?par=" + 
          escape((new Date()).toString());
}

Using the JavaScript as an embedded resource

I have made the JavaScript file an embedded resource to make it easier for deployment. To do that I had to do the following:

  1. Added a javascript file to the project library and called it JavaScript.js
  2. I made this file an embedded resource by selecting it and setting Build Action to Embedded Resource
  3. I have added the following line to the AssemblyInfo.cs : [assembly: WebResource("AT.Web.UI.Controls.JavaScript.js","application/x-javascript")]

Using the Control

  1. Copy session-keep-alive.ashx to your website.
  2. Reference AT.Web.UI.Controls.dll in your website.
  3. You can add this control to the VS.NET toolbox OR you could add it manually by adding <%@ Register TagPrefix="at" Namespace="AT.Web.UI.Controls" Assembly="AT.Web.UI.Controls" %> to the top of the page and adding ``anywhere in the html body. You might need to set the refresh Duration, AutoHide or PageUrl

Check the sample file for more information.

Credits

The initial code that I used two years ago was based on an article, Make session last forever, written by Gevorg on Code Project.

All suggestions and bug reports are welcome.

Download SessionKeepAlive.zip (19.55 kb)