LiveJournal Tags: ,,

Why a session less controller?

Update 5 Feb 2010 – The latest MVC 2 futures on CodePlex contains the the RenderAction() fix – this sample is no longer needed for a session less controller. Use the futures version.

You have a single client making multiple concurrent requests to the server. The default behavior is that these requests will be serialized; with the session less controller they execute in parallel.   For example, the client might make a single long-running request, but the developer wants other shorter requests (such as AJAX requests) from the client not to be blocked.  Normally, these later requests would be blocked from running until the first request completes due to a lock being taken on the user’s session.  The goal of the APIs being introduced here is to allow the developer to specify on a per-controller level what type of session will be required for the actions within that controller. A complete sample download is available on a link at the bottom of this page. The image below shows the session less controller sample rendered with FireFox.

When I first tried "Reload All Tabs" on the sample using FireFox, each tab was updated serially. It turns out FireFox and other browsers (but not IE8) automatically serialize requests to the same URL. To enable the tabs to refresh in parallel,  request the same URL but append a unique query string to each tab. The sample shows the dramatic improvement in refreshing all tabs with session disabled over the session enabled controller.

Enabling session less controllers in a MVC project

  1. Add a reference to the MVC Futures binary (MVC 2 RC).  The sample project includes the latest Microsoft.Web.Mvc.dll in its bin folder.
  2. Add the dynamic session module to the root Web.config file. The added lines are displayed with yellow highlighting below.
    <httpModules>
      <add name="ScriptModule" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
      <add name="UrlRoutingModule" type="System.Web.Routing.UrlRoutingModule, System.Web.Routing, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>  
      <add name="MvcDynamicSessionModule" type="Microsoft.Web.Mvc.MvcDynamicSessionModule, Microsoft.Web.Mvc, Version=2.0.0.0"/>
    </httpModules>
    <modules runAllManagedModulesForAllRequests="true">
        <remove name="ScriptModule"/>
        <remove name="UrlRoutingModule"/>
        <add name="ScriptModule" preCondition="managedHandler" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
        <add name="UrlRoutingModule" type="System.Web.Routing.UrlRoutingModule, System.Web.Routing, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/> 
        <add name="MvcDynamicSessionModule" type="Microsoft.Web.Mvc.MvcDynamicSessionModule, Microsoft.Web.Mvc, Version=2.0.0.0"/> 
    </modules>

    Note that this module must be declared after the Routing module.  If using ASP.NET 4, one or both of these <modules> elements might be empty and will need to be created.

  3. Replace the default controller factory in the Global.asax file. The changed lines are displayed with yellow highlighting below.
    protected void Application_Start() {
        AreaRegistration.RegisterAllAreas();
        RegisterRoutes(RouteTable.Routes);
        ControllerBuilder.Current.SetControllerFactory(new MvcDynamicSessionControllerFactory()); 
    }

Specifying session behavior on a controller

The  ControllerSessionState attribute specifies the session behavior for a controller.  The following snippet demonstrates disabling session state on a controller:

  [ControllerSessionState(ControllerSessionState.Disabled)]
  public class SessionStateDisabledController : Controller {
 

The ControllerSessionState enumeration is similar to the SessionStateBehavior enumeration introduced in ASP.NET 4.  It contains four values:

  • Default – Same as Required.
  • Required – A full Session lock is taken, and requests to this controller are serialized.  This is the normal MVC behavior.
  • ReadOnly – A Session lock is not taken, and a controller can read values in Session, but it cannot modify the values in Session.  Requests to this controller will be parallelized.
  • Disabled – Session is unavailable within this controller, and attempts to use it will be met with an error.  Requests to this controller will be parallelized.

Known Issues

  • The default implementation of TempData uses session for storage.  You will not be able to use the default TempData in a controller for which session is marked ReadOnly or Disabled.  To work-around this, avoid reading from or writing to TempData from these controllers.  Another option is to use a non-session-based TempData provider such as CookieTempDataProvider (also in MVC Futures).
  • RenderAction() cannot be used at all in an MVC application if the dynamic session module is also loaded.  This affects all requests, even requests for controllers that are not decorated with the ControllerSessionState attribute. The work around is to use the DynamicSessionRenderAction.zip project. The DynamicSessionRenderAction project builds Microsoft.Samples.Mvc.DynamicSession.dll, which is needed to support RenderAction without session.
    RenderAction + session-less instructions:
    1. In your MVC project, add a reference to Microsoft.Samples.Mvc.DynamicSession.dll and Microsoft.Web.Mvc.dll (AKA futures DLL).
    2. In the root Web.config file, use MvcDynamicSessionModule2 instead of MvcDynamicSessionModule. The replacement line is shown below.  <addname="MvcDynamicSessionModule2" type="Microsoft.Samples.Mvc.DynamicSession.MvcDynamicSessionModule2,
           Microsoft.Samples.Mvc.DynamicSession, Version=1.0.0.0
      " />
    3. In Global.asax, set the current controller factory to MvcDynamicSessionControllerFactory2.
      protected void Application_Start() {
          AreaRegistration.RegisterAllAreas();
          RegisterRoutes(RouteTable.Routes);
         ControllerBuilder.Current.SetControllerFactory(new MvcDynamicSessionControllerFactory2());
      }
       

Credits: Levi wrote the samples, futures code and sketched out the usage.