Resolving Controller Blocking within .NET 4.5 and ASP.NET MVC
While recently updating an MVC Project, several erratic issues began to start to arise involving inconsistent Controller calls, Controller blocking and apparent deadlocks that were basically rendering the application (and the poor browser handling it) useless.
The update was performed in two phases, the first being a simple ASP.NET MVC3 project deployment and the second phase was an update from .NET 4.0 to .NET 4.5, which is where things seemed to go awry and our story begins.
Update : A hotfix for this issue has been released, you can read more about it in this follow-up post.
The Problem
After updating to .NET 4.5, Internet Explorer 9 and below began encountering some strange issues regarding blocked calls to Controllers within the application. The problem was typically occurring when an AJAX request was made to a Controller and during that period an additional request was made to the same controller, which would cause the browser to lock up and typically hang between 1-2 minutes prior to resolving the issues and executing the queued actions.
An Example Scenario
The following is a set of actions that a user might take to reproduce this issue and should serve as a guideline to determine if this same problem is affecting you :
- User clicks a link that will temporarily store a value in the ViewData to be accessed on the next Request.
- A Redirect occurs and another Controller is accessed and after loading, an AJAX call is made.
- During this AJAX call, which accesses the ViewData value properly, an additional call is quickly made (e.g. a user immediately clicking a link to navigate to another Controller). After clicking this link, the deadlock will occur and the browser will become unresponsive.
or
- The call will be successfully performed as long as it is not interrupted by another request, but attempting to perform the previous steps again will fail.
Each page in question that was causing these issues was each doing one of the following two things :
- The View contained a jQuery-powered Grid that would perform an AJAX call to populate the Grid contents upon the page loading.
- The Controller Action typically was reading an item from the ViewData within these Actions.
An Investigation Ensues
After continually looking at different things that could be causing this issue such as the AJAX calls themselves, the SessionState settings for the Application, Caching, Temporary Storage (e.g. ViewData, TempData and Session) nothing seemed to make sense.
I attempted to use Fiddler however was let down as the traffic would not reveal anything. Developer Tools and Fiddler would both show the AJAX call being made to the Controller Action, which contained a breakpoint, however the breakpoint would never be hit (at least not until the blocking issue was resolved).
Here are a few of the attempted fixes :
- AJAX Calls I reviewed over the AJAX calls that were occurring and attempted to change several of the properties and parameters that were being passed into the call without any luck. I hoped that by setting the cache to false that would ensure that the AJAX requests were not being cached (as IE can be notorious for caching GET requests). I also attempted to use the timeout property in hopes that when the request timed out that it would hopefully break up the "blocking" effect :
// Disabled caching and forced an explicit timeout on the calls
$.ajaxOptions({ cache: false, timeout: 1000 });
- SessionState The fact that the Controllers within the application were clearly blocking one another made me think that the culprit was the SessionState and by setting it to ReadOnly or Disabled that this would fix the problem (especially due to the fact that the ViewData was being accessed within the Controller) :
[Authorize]
[SessionState(SessionStateBehavior.ReadOnly)]
public class MyController : ApplicationController
- Temporary Storage Access I thought that the issue may have been due to the use of Temporary Storage within the application so I attempted to switch out the ViewData with the Session to temporarily store some values being passed across but I was still met with nothing but defeat :
// Trying all three of these, but still met with failure
ViewData["YourKey"] = yourValue;
Session["YourKey"] = yourValue;
TempData["YourKey"] = yourValue;
None of these provided any type of resolution to the issue, so I thought I would consider looking into IIS directly to determine if the issue was being caused at that level.
Finally! Some Classical Salvation!
After doing some significant damage to my desk and debating doing the same to my laptop, I continued to rethink what could possibly be going so wrong and then I stumbled upon a suggestion that cured all of these developmental ailments : Change the Application Mode within the Application Pool Settings in IIS from Integrated to Classic.
If you are experiencing these same issues with your application and are running into very bizarre and difficult to track down problems regarding Controller blocking, try changing the Mode on your Application Pool within IIS to Classsic Mode. It requires a bit of tinkering when working with MVC to handle routing the old school way: wildcard mappings.
I've found that this is the only working solution that I have encountered aside from uninstalling .NET 4.5. Although, I am not quite sure of all of the details as to why IE9, .NET4.5 and AJAX don't seem to want to play nice with one another, I am certainly glad to have this issue resolved.
I still believe that this issue could in fact be a bug involving the SessionState and how Internet Explorer 9 interacts with the updated mechanisms to implement asynchronous calls with the recent .NET 4.5 release. If any Microsoft folks are out there and would care to elaborate a bit more on the issue, I would be glad to create a proof-of-concept.
Update with a New and Improved Fix!
After some follow-up on this issue by several great members of the ASP.NET Forums as well as a few Microsoft employees, we were able to narrow down a more specific fix that involved the uploadReadAhead property within the web.config :
<system.webServer>
<serverRuntime uploadReadAheadSize="0" />
</system.webServer></pre>
It should be noted that to apply this change, you will likely need to enable this property to be overridden by making a change within IIS, you'll just need to change the <serverRuntime>
property within your applicationHost.config
file to Allow
instead of Deny :
<section name="serverRuntime" overrideModeDefault="Allow" />
After some additional testing, I was completely unable to reproduce the issue and it appears that this is a pretty solid fix to the problem.