51

I am creating an ActionResult in ASP.Net MVC to serve images. With Session state enabled, IIS will only handle one request at a time from the same user. (This is true not just in MVC.)

Therefore, on a page with multiple images calling back to this Action, only one image request can be handled at a time. It's synchronous.

I'd like this image Action to be asynchronous -- I'd like multiple image requests to each execute without needing the previous one to complete. (If the images were just static files, IIS would serve them up this way.)

So, I'd like to disable Session just for calls to that Action, or to specify that certain requests do not have Session state. Anyone know how this is done in MVC? Thanks!

Martin
  • 2,180
  • 4
  • 21
  • 41
Matt Sherman
  • 8,298
  • 4
  • 37
  • 57

9 Answers9

57

If anyone is in the situation I was in, where your image controller actually needs read only access to the session, you can put the SessionState attribute on your controller

[SessionState(SessionStateBehavior.ReadOnly)]

See http://msdn.microsoft.com/en-us/library/system.web.mvc.sessionstateattribute.aspx for more info.

Thanks to https://stackoverflow.com/a/4235006/372926

Community
  • 1
  • 1
SamStephens
  • 5,721
  • 6
  • 36
  • 44
37

Rather than implementing an action filter for this, why don't you implement a RouteHandler?

Here's the deal - IRouteHandler has one method - GetHttpHandler. When you make an ASP.Net MVC request to a controller, by default the routing engine handles the request by creating a new instance of MvcRouteHandler, which returns an MvcHandler. MvcHandler is an implementation of IHttpHandler which is marked with the (surprise!) IRequiresSessionState interface. This is why a normal request uses Session.

If you follow my blog post on how to implement a custom RouteHandler (instead of using MvcRouteHandler) for serving up images - you can skip returning a session-tagged IHttpHandler.

This should free IIS from imposing synchronicity on you. It would also likely be more performant because it's skipping all the layers of the MVC code dealing with filters.

Dbl
  • 5,634
  • 3
  • 41
  • 66
womp
  • 115,835
  • 26
  • 236
  • 269
  • wow, great blog post. it seems that using a standard MVC controller, Session dependence would be unavoidable..? – Matt Sherman Sep 23 '09 at 07:09
  • Yeah. Luckily, they made MVC as extensible as possible so it's not too much work to bypass anything you don't like. – womp Sep 23 '09 at 14:45
  • Thanks! It was a pain for me when i turn session off and Action returning image was mixed the results (may images on page using this action). – Victor Gelmutdinov Oct 17 '10 at 17:37
  • @rwalter you can replace your comment now. replaced the link with an archived one – Dbl Mar 16 '15 at 08:34
  • 3
    Worth knowing: http://www.c-sharpcorner.com/UploadFile/ff2f08/session-state-behavior-per-action-in-Asp-Net-mvc/ – Dbl Mar 16 '15 at 08:41
  • Very nice indeed Dbl, gives the most easy to (re)use flexibility of decorating individual controller methods with a specific sessions state behaviour. – Youp Bernoulli Sep 22 '16 at 07:03
  • Another solution, using the `SessionState` attribute can be that you provide a `lock` over your critical section (i.e. reading/writing to the session). Which allows concurrent requests to the sever, however, making modifications to the `session` atomic. – ethane Apr 09 '18 at 11:46
9

I also came across the same problem and after doing R&D this link worked for me Reference: https://techatfingers.wordpress.com/2016/06/14/session-state-on-action/

  1. Create custom Attribute
  2. Override the “GetControllerSessionBehavior” method present in class DefaultControllerFactory.
  3. Register it in global.aspx

1> Create custom Attribute

public sealed class ActionSessionStateAttribute : Attribute
    {
            public SessionStateBehavior SessionBehavior { get; private set; }          
            public ActionSessionStateAttribute(SessionStateBehavior sessionBehavior)
            {
                SessionBehavior = sessioBehavior;
            }
    }

2. Override

public class SessionControllerFactory : DefaultControllerFactory
{       
        protected override SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, Type controllerType)
        {
            if (controllerType == null)
                return SessionStateBehavior.Default;

            var actionName = requestContext.RouteData.Values["action"].ToString();
            Type typeOfRequest=requestContext.HttpContext.Request.RequestType.ToLower() =="get"?typeof(HttpGetAttribute):typeof(HttpPostAttribute);
            // [Line1]
            var cntMethods = controllerType.GetMethods()
                   .Where(m => 
                    m.Name == actionName &&
                    (  (  typeOfRequest == typeof(HttpPostAttribute) && 
                          m.CustomAttributes.Where(a => a.AttributeType == typeOfRequest).Count()>0
                       )
                       ||
                       (  typeOfRequest == typeof(HttpGetAttribute) &&
                          m.CustomAttributes.Where(a => a.AttributeType == typeof(HttpPostAttribute)).Count() == 0
                       )
                    )
                );
            MethodInfo actionMethodInfo = actionMethodInfo = cntMethods != null && cntMethods.Count() == 1 ? cntMethods.ElementAt(0):null;
            if (actionMethodInfo != null)
            {
                var sessionStateAttr = actionMethodInfo.GetCustomAttributes(typeof(ActionSessionStateAttribute), false)
                                    .OfType<ActionSessionStateAttribute>()
                                    .FirstOrDefault();

                if (sessionStateAttr != null)
                {
                    return sessionStateAttr.Behavior;
                }
            }
            return base.GetControllerSessionBehavior(requestContext, controllerType);
 }

3. Register class in Global.asax

public class MvcApplication : System.Web.HttpApplication
 {
        protected void Application_Start()
        {
            // --- other code ---
            ControllerBuilder.Current.SetControllerFactory(typeof(SessionControllerFactory));
        }
}
SeeTheC
  • 1,560
  • 12
  • 14
7

Try serving the images from another domain. So something like images.mysite.com.

This will provide you two benefits: One, sessions are tracked by a cookie, so images.mysite.com won't have the cookie. Two, it will give you an additional two concurrent requests to retrieve images.

Have you considered setting up a HttpHandler to serve up your images?

Chuck Conway
  • 16,287
  • 11
  • 58
  • 101
  • that's an interesting idea. i could disable session state across-the-board for that app/site. i would still like to know if the per-request/per-action thing is possible, too. – Matt Sherman Sep 23 '09 at 06:19
5

SessionState attribute is quite helpful if u use mvc3. How to achieve this with mvc2 needs a little more coding.

Idea is to tell the asp.net that specific request wont use session object.

So, Create a custom route handler for specific requests

public class CustomRouteHandler : IRouteHandler
    {
        public System.Web.IHttpHandler GetHttpHandler(RequestContext requestContext)
        {
            requestContext.HttpContext.SetSessionStateBehavior(System.Web.SessionState.SessionStateBehavior.ReadOnly);
            return new MvcHandler(requestContext);
        }
    }

SessionStateBehavior enum has 4 members, you should use "disabled" or "readonly" modes to get async behavior.

After creating this custom route handler, be sure that your specific requests goes through this handler. This can be done via defining new routes at Global.asax

routes.Add("Default", new Route(
                "{controller}/{action}",
               new RouteValueDictionary(new { controller = "Home", action = "Index"}),
               new CustomRouteHandler()
                ));

Adding this route makes all your requests to be handled by your custom route handler class. You can make it specific by defining different routes.

Serdar
  • 71
  • 1
  • 3
  • 1
    Your solution is for mvc 3+, would you share how to do in mvc 2 as you write that it could be achieved in mvc 2 w/ little more coding? – adardesign Dec 11 '15 at 17:23
  • Just in case it's of value to anyone, this still works for me in MVC 5.2.3.0. I did however have to change the `SessionStateBehaviour` to `Disabled` and add the route in `RouteConfig.cs` (method `RegisterRoutes(RouteCollection)`) instead of in `Global.asax`. – MeterLongCat Oct 04 '17 at 03:41
3

Change DefaultCOntrollerFactory to custom ControllerFactory class. Default Controller.TempDataProvider use SessionStateTempDataProvider. you can change it.

1.Set web.config/system.web/sessionState:mode="Off".

2.create DictionaryTempDataProvider class.

  public class DictionaryTempDataProvider : ITempDataProvider
  {
    public IDictionary<string, object> LoadTempData(ControllerContext controllerContext)
    {
      return new Dictionary<string, object>();
    }

    public void SaveTempData(ControllerContext controllerContext, IDictionary<string, object> values)
    {
    }
  }

3.Create DictionaryTempDataControllerFactory

  public class DictionaryTempDataControllerFactory : DefaultControllerFactory
  {
    public override IController CreateController(System.Web.Routing.RequestContext requestContext, string controllerName)
    {
      var controller = base.CreateController(requestContext, controllerName) as Controller;
      if (controller!=null)
        controller.TempDataProvider = new DictionaryTempDataProvider();

      return controller;
    }
  }

4.In global.asax.cs Apprication_Start event set DictionaryTempDataControllerFactory.

protected void Application_Start()
{
  RegisterRoutes(RouteTable.Routes);

  ControllerBuilder.Current.SetControllerFactory(
   new DictionaryTempDataControllerFactory()
  );
}
takepara
  • 10,413
  • 3
  • 34
  • 31
1

On our server, IIS doesn't even know about sessions - it's the ASP.NET stack that handles one request per session at a time. Static files, like images, are never affected.

Is it possible that your ASP.NET app is serving the files instead of IIS?

orip
  • 73,323
  • 21
  • 116
  • 148
  • that's correct, it's ASP that enforces the session/synchronous stuff, not IIS itself. the solution is to go earlier in the pipeline as womp described above. – Matt Sherman Sep 23 '09 at 22:18
1

Create new Controller

Decorate controler with [SessionState(SessionStateBehavior.Disabled)]

Refactor code you want seesion stated disabled for to that controller

Diemar
  • 31
  • 1
0

I would to share my solution for disable ASP.NET Session for an specific request (in my case, a WCF Service) using an HttpModule:

public class AspNetSessionFilterModule : IHttpModule
{
    public void Init(HttpApplication context)
    {
        context.PostMapRequestHandler += OnPostMapRequestHandler;
    }

    private void OnPostMapRequestHandler(object sender, EventArgs e)
    {
        var context = (sender as HttpApplication).Context;
        DisableSessionForSomeRequests(context);
    }

    private void DisableSessionForSomeRequests(HttpContext context)
    {
        if ("~/Services/MyService.svc".Equals(context.Request.AppRelativeCurrentExecutionFilePath, StringComparison.InvariantCultureIgnoreCase))
        {
            context.SetSessionStateBehavior(System.Web.SessionState.SessionStateBehavior.Disabled);
        }
    }

    public void Dispose()
    { }
}