Mike Hadlow posted about the out of the box ASP.NET MVC support (AsyncControllers) for developing scalable web tiers. He gives reasons why the “old” APM style of writing AsyncControllers is cumbersome and then shows how can we utilize TPL library to make code more maintanable and laments that MVC still doesn’t have full/deep support for TPL Library… Craig Cavalier takes it from there and, with the help of ASP.NET MVC Futures and TPL Extensions Library shows how that works end to end. Very nice… I had to try it. Got a test mvc site with simple example that uses “old” style AsyncController approach and that shows which part is executing from which thread. This is the result:
Seems, fine. We get to not block on the ThreadPool thread – as designed for. Code is old APM style, and this is how the code for this controller looks like:
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Mvc;
namespace MvcApplication3.Controllers {
public class HomeController : AsyncController {
[HttpGet]
public void IndexAsync() {
AsyncManager.OutstandingOperations.Increment();
ViewData["indexAsync"] = Helper.GetThreadInfo("IndexAsync");
Task.Factory.StartNew(LongOperation, TaskCreationOptions.LongRunning) ;
}
public ActionResult IndexCompleted(string reault) {
ViewData["longOperation"] = AsyncManager.Parameters["result"];
ViewData["indexCompleted"] = Helper.GetThreadInfo("IndexCompleted");
return View();
}
void LongOperation() {
try {
Thread.Sleep(1234);
AsyncManager.Parameters["result"] = Helper.GetThreadInfo("LongOperation");
}
finally {
AsyncManager.OutstandingOperations.Decrement();
}
}
}
}
Then I tried to do the same for Craig’s example… and that doesn’t look good, we are now just grabbing additional thread from the thread pool, while blocking on the original thread. The very reason why AsyncController was there in the first place, seems to be negated.
Here are the results (tried many times with the same results):

Here is the code for this case:
using System.Linq;
using System.Threading.Tasks;
using System.Web.Mvc;
using AsyncControllerHelper;
using Suteki.AsyncMvcTpl.Services;
namespace Suteki.AsyncMvcTpl.Controllers {
public class HomeController : Controller {
readonly UserService userService = new UserService();
public HomeController() {
IControllerDescriptorCache descriptorCache = new AsyncControllerDescriptorCache();
ActionInvoker = new BetterAsyncControllerActionInvoker(descriptorCache);
}
[HttpGet]
public Task<ViewResult> Index() {
ViewData["indexAsyncPre"] = Helper.GetThreadInfo("IndexPre");
var _view = from user in userService.GetCurrentUser()
select View(user);
ViewData["indexAsyncPost"] = Helper.GetThreadInfo("IndexPost");
return _view;
}
[HttpGet]
public Task DoStuff() {
var doStuff = from user in userService.GetCurrentUser()
from _ in userService.SendUserAMessage(user, "Hi From the MVC TPL experiment2")
select View("index", user);
return doStuff.ContinueWith(x => { });
}
}
}
I may be doing something wrong. Will check later – it is Sunday afternoon after all…
From mike’s post:
“… You should only ever use this technique if you have an immediate scalability concern and you know that it is caused by a threadpool full of threads blocked by long running IO operations.”
Mike’s original post.