Reusable embedded keep session alive with HtmlHelper for ASP.NET MVC 1.0 C#

Overview

First off let me state that I wouldn't advise using Session variables in an ASP.NET web application for anything business related. Session is helpful for storing a user's breadcrumb trail or other minor settings. As a simple rule, if a user of your web application loses his or her session, it should only be a minor setback, and should not be a devastating problem that causes data loss or other major issues. For example, if a session is lost, the user's breadcrumb trail should just be rebuilt (if possible), or just put back to a state that may not be as helpful as before, but is still somewhat helpful. As another example, the user might simply lose his or her last search settings. At any rate, it should definitely never cause an error or exception to occur.

I spent many hours combing through solutions to this problem on the web. Eventually I put together some code that worked well for ASP.NET web forms. When we moved our projects to ASP.NET MVC 1.0, it was only natural to come up with a way that fit into the MVC framework. This is that solution.

MyLibrary Solution

I've designed this solution so that it could be placed in a separately maintained library and used by any of your projects. That said, I've named my code library "MyLibrary". As a standard way of preventing name clashes, I've also prefixed every class and file in this solution with "MyLibrary". Here is a screenshot of the Solution Explorer (Visual Studio 2008 / C#)...the actual code will follow later in this article:

How to use in your project

Download the MyLibrary solution.

After you create and build "MyLibrary", you just need to add a reference to MyLibrary.Web.dll in your project, and call the HtmlHelper. I typically put it in my master files as follows. The key lines are 3 and 8:

  1. <%@ Master Language="C#" Inherits="System.Web.Mvc.ViewMasterPage" %>
  2.  
  3. <%@ Import Namespace="MyLibrary.Web.Mvc" %>
  4.  
  5. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
  6. <html xmlns="http://www.w3.org/1999/xhtml">
  7. <body>
  8. <%= Html.KeepSessionAlive(Context) %>
  9. </body>
  10. </html>

Source Code

MyLibrary\MyLibrary.Web\Mvc\Controllers\MyLibraryMvcKeepSessionAliveController.cs

This will make the view refresh itself 10 seconds before the session times out.

  1. using System;
  2. using System.Web.Mvc;
  3.  
  4. namespace MyLibrary.Web.Mvc.Controllers
  5. {
  6. public class MyLibraryMvcKeepSessionAliveController : Controller
  7. {
  8. public ActionResult Index()
  9. {
  10. Response.AddHeader("Refresh", Convert.ToString(((Session.Timeout * 60) - 10)));
  11. return this.PluginView();
  12. }
  13. }
  14. }

MyLibrary\MyLibrary.Web\Mvc\Views\MyLibraryMvcKeepSessionAlive\Index.aspx

This is the actual view that will refresh to keep the session alive. The user will not see this on their page, because it will be in an iframe with a border, width, and height of zero.

NOTE: This view's "Build Action" property should be set to "Embedded Resource".

  1. <%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage" %>
  2.  
  3. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  4.  
  5. <html xmlns="http://www.w3.org/1999/xhtml" >
  6. <head id="Head1" runat="server">
  7. <title>Keep Session Alive</title>
  8. <meta http-equiv="expires" content="Sun, Dec 31 1970 12:00:00 GMT" />
  9. <meta http-equiv="cache-control" content="no-cache" />
  10. <meta http-equiv="pragma" content="no-cache" />
  11. </head>
  12. <body>
  13. <form id="form1" runat="server"></form>
  14. </body>
  15. </html>

MyLibrary\MyLibrary.Web\Mvc\MyLibraryMvcExtensions.cs

  1. using System.Web.Mvc;
  2.  
  3. namespace MyLibrary.Web.Mvc
  4. {
  5. public static class MyLibraryMvcExtensions
  6. {
  7. /// <summary>
  8. /// Simplifies the process of returning a view that is marked as an embedded resource.
  9. /// </summary>
  10. public static ViewResult PluginView(this Controller controller)
  11. {
  12. string controllerName = controller.RouteData.GetRequiredString("controller");
  13. string viewName = controller.RouteData.GetRequiredString("action");
  14. string assemblyName = controller.GetType().Assembly.FullName.Split(',')[0];
  15. string pluginPath = string.Format(
  16. @"~/Plugin/{0}.dll/{0}.Web.Mvc.Views.{1}.{2}.aspx",
  17. assemblyName, controllerName, viewName
  18. );
  19.  
  20. return new ViewResult
  21. {
  22. ViewName = pluginPath,
  23. ViewData = controller.ViewData,
  24. TempData = controller.TempData,
  25. };
  26. }
  27. }
  28. }

MyLibrary\MyLibrary.Web\Mvc\MyLibraryMvcHtmlHelpers.cs

This is the HtmlHelper that can be used on any view in your project. I prefer to use it on the Site.master page so I don't have to have it on every page.

  1. using System.Web;
  2. using System.Web.Mvc;
  3.  
  4. namespace MyLibrary.Web.Mvc
  5. {
  6. public static class MyLibraryMvcHtmlHelpers
  7. {
  8. public static string KeepSessionAlive(this HtmlHelper helper, HttpContext context)
  9. {
  10. UrlHelper urlHelper = new UrlHelper(helper.ViewContext.RequestContext);
  11. string url = urlHelper.Action("Index", "MyLibraryMvcKeepSessionAlive");
  12. return "<iframe id=\"KeepSessionAlive\" src=\"" + url + "\" frameborder=\"0\" width=\"0\" height=\"0\" runat=\"server\"></iframe>";
  13. }
  14. }
  15. }

Other settings required for this to work

Make sure your application is running in its own IIS application pool. By default, an IIS application pool will occasionally recycle itself...which kills all active sessions. You will need to disable all recycling. You can change the session timeout to a small number...i.e., 5 minutes. As long as we have the keep session alive code refreshing the session just before it expires, you can use a small session timeout. This will ensure that you do not have a build up of sessions hanging around, and will allow your application pool to shut down more often due to inactivity, thus keeping it healthy.

kick it on DotNetKicks.com