0

I've just known about Session State blocking, so I'm trying to test disabling that kind of blocking with a simple MVC project (the default when creating a new MVC project).

Here is the HomeController with the only involved About action method:

[SessionState(System.Web.SessionState.SessionStateBehavior.Disabled)]
public class HomeController : Controller
{
    public ActionResult Index()
    {
        return View();
    }
    static int c = 0;
    static long ci;
    [Authorize]
    public ActionResult About()
    {
        var isSecondRequest = ++c == 2;
        //Session["a"] = 1;
        Debug.Print(">>> Started working ..." + DateTime.Now);
        if (isSecondRequest)
        {
            Debug.Print(">>> current counter: " + ci);
            for (long i = 0; i < 10000000000; i++) ;
        }            
        else
        {
            for (ci = 0; ci < 10000000000; ci++) ;
        }
        Debug.Print(">>> Finished!");
        return Content("welcome");
    }

}

I test this by firstly opening two tabs of the home page. Then try clicking on About link on the first tab and almost immediately switch to the second tab to click on About one more time. So I expect the line >>> Started working … should be printed 2 times with timestamps almost the same or having a tiny difference of about 1 or 2 seconds, but the actual difference is up to 21 seconds, like this:

>>> Started working ...4/7/2019 3:16:15 PM
>>> Started working ...4/7/2019 3:16:36 PM

So that means the second request is still blocked, well that's unexpected when I've already applied the attribute SessionStateAttribute with behavior of Disabled to the HomeController. And really looks like it does work in another sense that the Session is null for each request.

Could you spot anything wrong here? My overall purpose is to disable this kind of blocking so that I can manually manage the concurrent requests and immediately return some helpful message (such as for the second request) to the client so that it's not looking like busy unnecessarily.

Hopeless
  • 4,397
  • 5
  • 37
  • 64
  • In principle, it looks correct. However, your for loop Max be hogging your processor time. Please try it again with a simple Thread.Sleep(5000) and see what happens then. – Christoph Herold Apr 07 '19 at 09:23
  • Which version of the .Net framework are you targeting? We solved a similar problem from one of my questions : https://stackoverflow.com/questions/42300438/acquirerequeststate-vs-preexecuterequesthandler/55331786#55331786 – Martin Venter Apr 07 '19 at 09:23
  • @ChristophHerold I've just tried using only `Thread.Sleep(5000)` as you suggested and now the difference is exactly `5` seconds, so the second is still blocked. – Hopeless Apr 07 '19 at 09:27
  • @MartinVenter my test project targets .NET 4.5.2 – Hopeless Apr 07 '19 at 09:28
  • If you can upgrade to 4.6.2 you can attempt the session provider allowing concurrent requests via "aspnet:AllowConcurrentRequestsPerSession" for science? It solved quite a few problems for us on blocked sessions. PS: Do you have anything hooked into something in the global.asax that might be causing this block rather than session? – Martin Venter Apr 07 '19 at 09:32
  • @MartinVenter I'll try the solution from your shared link, however does that mean we can just disable this kind of blocking after upgrading to .NET 4.6.2? whereas the attribute `SessionStateAttribute` is already available in .NET 4.5.2 but still useless? As I said my test project is just the default one when you create a new MVC project, it's just lightweight and should not hook anything heavyweight in global.asax – Hopeless Apr 07 '19 at 09:34
  • Yep, that's a good point and I understand your frustration, I've spent weeks on this problem before. Do you have the same behavior when removing the [authorize] attribute? – Martin Venter Apr 07 '19 at 09:37
  • @MartinVenter removing that attribute does not make any difference, still blocked. – Hopeless Apr 07 '19 at 09:39
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/191388/discussion-between-martin-venter-and-hopeless). – Martin Venter Apr 07 '19 at 09:44

2 Answers2

0

As per our chat, this is due to this being on the same controller and not actually directly related to session. When moving this to a separate controller the two requests do run at the same time:

public class SecondaryController : Controller
{
    // GET: Secondary
    public ActionResult Index()
    {
        Debug.Print(">>> Started working ..." + DateTime.Now);
        Thread.Sleep(10000);
        Debug.Print(">>> Finished!");
        return Content("welcome");
    }
}

Along with

[SessionState(System.Web.SessionState.SessionStateBehavior.ReadOnly)]
public class HomeController : Controller
{
    public ActionResult Index()
    {
        return View();
    }


    public ActionResult About()
    {
        Debug.Print(">>> Started working ..." + DateTime.Now);
        Thread.Sleep(10000);
        Debug.Print(">>> Finished!");
        return Content("welcome");
    }

}

Is allowing me to do this just fine, showing the following output :

Started working ...4/7/2019 1:01:49 PM

Started working ...4/7/2019 1:01:52 PM

Finished!

Finished!

The following might be of interest here

"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."

Taken from Disable Session state per-request in ASP.Net MVC

Martin Venter
  • 231
  • 2
  • 12
  • I really don't understand the point of using 2 separate controllers here. How could you map 1 URL (used on client side) to 2 separate controllers? Also my point is only one controller should be involved here. From the link at the end of your answer, I've also tried creating a custom `IRouteHandler` as in the answer of *Serdar* but it's still not working. At least that way the default MvcRouteHandler is not used, so the `IRequiresSessionState` should not be involved here. Really I still believe that the `SessionStateAttribute` should work (as suggested by *SamStephens*), but somehow it doesn't. – Hopeless Apr 07 '19 at 12:25
0

I'm really not sure, what you're doing wrong. I just created a completely new MVC application using VS2017. I took the HomeController and added the [SessionState(SessionStateBehavior.Disabled)] attribute. Then, I added the following code to the Index method:

public ActionResult Index()
{
    this.ViewBag.Start = DateTime.Now;
    System.Threading.Thread.Sleep(5000);
    this.ViewBag.End = DateTime.Now;
    return View();
}

In my view, I added:

<div class="row">
    <div class="col-xs-6">Start: @this.ViewBag.Start.ToString("HH:mm:ss.fffffff")</div>
    <div class="col-xs-6">End: @this.ViewBag.End.ToString("HH:mm:ss.fffffff")</div>
</div>

Now, I started up the application, opened two tabs, and loaded them simultaneously. The result was:

Tab1: Start: 21:10:41.3620017, End: 21:10:46.3623676
Tab2: Start: 21:10:42.4819327, End: 21:10:47.4824334

So, you can easily see, they ARE processing simultaneously.

I have had exactly this use case in various of my applications for a few years now, and it has been working fine. The [Authorize] also should not account for the serialized processing, as I do this in my applications, and I've never had problems there.

I also tried it with your code (with the for loop changed to Thread.Sleep) and Windows Authentication. It works as it should. In my other projects I typically use Forms authentication, and I've also never had problems with it. What authentication are you using?

Christoph Herold
  • 1,799
  • 13
  • 18
  • almost are default so the authentication type I use is `ApplicationCookie`. – Hopeless Apr 08 '19 at 00:12
  • well it's really strange that when I try using Edge instead of Chrome, it seems to work BUT actually not, because even when I don't use the `SessionStateAttribute`, it's still not blocked and I can debug and see that each request has different `SessionId` (so of course the second will not be blocked). Now it's hard to understand why the 2 requests from one same browser with one same logged-in user can have different session id? So looks like browser is also what may cause the original issue :( – Hopeless Apr 08 '19 at 00:38
  • Actually, that is a good point. AFAIK, as long as you have not written anything to the session, ASP.NET is intelligent enough not to actually start one, so you will not have a session cookie available. Therefore, there will be no blocking. I will check again with an initialized session and let you know my results. – Christoph Herold Apr 08 '19 at 09:32
  • the problem is 2 browsers act differently against one same code – Hopeless Apr 08 '19 at 09:43
  • Maybe, the one browser still had a session cookie set from earlier development. That one will be kept. The other was clean and therefore gets a new session for every call. Try cleaning out all cookies and application data from your Chrome browser. It should then also always get a new session. – Christoph Herold Apr 08 '19 at 09:46
  • I just tested with a set session. The Session Cookie is identical in both tabs, but the behavior for the sessionless HomeController is as expected: The requests are processed simultaneously, not serialized. – Christoph Herold Apr 08 '19 at 15:33
  • Thank you for your help, looks like some strange issue is rooted from the cached cookie or something like that, I'll try investigating more on this and give you the feedback later. I have one day-off tomorrow so I can only continue to test on the day after tomorrow. – Hopeless Apr 08 '19 at 15:48
  • Well I've tried clearing the cookie (for localhost) in Chrome, then tried logging in again with my test user and repeated the steps to test the behavior of session blocking but looks like it's still very very strange. I'm not sure what's wrong with ASP.NET or with the Chrome browser, no matter the `SessionStateAttribute` is used or not, the second request is always blocked, the only sign of difference between using and not using `SessionStateAttribute` (with behavior of Disabled) is the `Session` being null or not null respectively. – Hopeless Apr 11 '19 at 16:42