Asynchronous MVC using the Task Parallel Library

I’m not going to go into any detail in this post as to why asynchronous actions may be beneficial to your application; other people have covered that in more detail than I’d care to go into. This post will however, try to show how implementing Async controllers can be made simpler, using the Task Parallel Library in .NET 4.0 and a little bit of MVC trickery.

Before I go too far, I’d like to point you to have a look at an introduction in using the TPL with MVC from Mike Hadlow. Mike’s post shows how you can use the TPL to simplify the consumption of Asynchronous services, from an Asp.Net Mvc AsyncController.

In Mike’s example, he ends up with the following AsyncController implementation:

public class HomeController : AsyncController

{

    readonly UserService userService = new UserService();

 

    [HttpGet]

    public void IndexAsync()

    {

        AsyncManager.OutstandingOperations.Increment();

        userService.GetCurrentUser().ContinueWith(t1 =>

        {

            var user = t1.Result;

            userService.SendUserAMessage(user, "Hi From the MVC TPL experiment").ContinueWith(t2 =>

            {

                AsyncManager.Parameters["user"] = user;

                AsyncManager.OutstandingOperations.Decrement();

            });

        });

       

    }

 

    public ViewResult IndexCompleted(User user)

    {

        return View(user);

    }

}

 

As you can see, we unfortunately have to abandon the succinctness of the TPL syntax, and revert back to the Asynchronous Programming Model pattern of having a start method with a completion callback.

Mike points out that it would be rather nice if instead, we could write a controller action that returns a Task result instead – our resultant action would be nice and simple:

[HttpGet]
public Task<ViewResult> Index()
{
    return from user in userService.GetCurrentUser()
           from _ in userService.SendUserAMessage(user, "Hi From the MVC TPL experiment")
           select View(user);
}

Even though this will compile (when using the TPL extension extras library), unfortunately, this will not run, as the default controller action invoker for running async actions (AsyncControllerActionInvoker) does not know how to handle Tasks…

Asp.Net Mvc Futures

The asp.net mvc futures project currently includes some more flexibility in its support for async patterns. Rather than being limited to supporting the “Async –> Completed” action pairs, the futures project contains the following options:

 

  • The Async Pattern (BeginFoo/EndFoo)
  • The event pattern (Foo/FooCompleted)
  • The Delegate Pattern (returning a Func<> that represents a continuation).

The most interesting of these techniques (to this example) is the Delegate Pattern:

[HttpGet]
public Func<int> Foo(int id)
{
    return () => id * 2;
}

Using this approach, a controller can specify a delegate to provide a completion callback. It’s not too much of a leap to see the similarities with this, and the desired technique using Tasks.

So how does the Asp.Net Futures project add these additional features?  Perhaps we can extend them to support TPL?

Hooking in to the Action Invoker

To support the additional async patterns using the futures project, a Controller must inherit the AsyncController from the futures assembly. This controller overrides a property from the base controller class in order to specify a new Action Invoker that can correctly identify actions that represent asynchronous methods. To identify these actions, the AsyncActionInvoker delegates to a AsyncActionMethodSelector that will reflect over the methods on a controller, and pick our asynchronous actions based on naming conventions, or the return type in the case of the delegate pattern. This seems like a good place to start looking to add our new feature.

Unfortunately for us, the AsyncActionMethodSelector does not delegate out to individual objects to identify and handle each type of asynchronous pattern, so supporting an additional pattern will involve some changes to this class. Following the Open/Closed principle could really have cleared up the design here…

Anyway, once we have extended to the AsyncActionMethodSelector to support our new pattern, we need to hook this back into the AsyncActionInvoker (which again, requires some code changes to this class – seriously, this code could’ve been much simpler if people followed SOLID principles!), and then we can use this new invoker from our Async Controller.

Using this new invoker, our controller can now support the use of Tasks for Asynchronous Actions!

Conclusion

As with other areas of the MVC framework, extending the framework to add new features like supporting Tasks to represent Asynchronous Controller Actions was a little bit more painful than it could have been, but in the end, it’s worth it to clean up the the required code to support asynchronous controller actions.

To check out the implementation of all this, you can get the full sample source from GitHub:

https://github.com/CraigCav/Suteki.AsyncMvcTpl

About these ads

About craigcav

Craig Cavalier works as a Software Developer for Liquid Frameworks in Houston Tx, developing field ticketing and job management solutions for industrial field service companies.

Posted on December 23, 2010, in ASP.NET MVC and tagged , . Bookmark the permalink. 8 Comments.

  1. Have you worked with the new AsyncCTP yet? Wondering how it affects your take on it?

  2. I pulled your code into my solution and have now got ASP.NET MVC 3 running through the Async CTP. Saved me loads of headaches, thanks.

    • Also have noticed a few other things:

      actions with parameters don’t work, had to rip a few more internal pieces to build the parameter descriptors for ReflectedTaskPatternActionDescriptor.GetParameters()

      System.Web.HttpContext.Current is null from the task executor thread so any of the old asp.net classes relying on it break in the current implementation. Had to bind the HttpContext manually via System.Web.HttpContext.Current = controllerContext.HttpContext.ApplicationInstance.Context; within the delegates that actually execute the start / end

      I built out the ReflectedTaskPatternActionDescriptor.ActionName property but can’t say i know that this was necesarry.

  3. Nice work, thanks a lot

    Action filters not working, overriding the GetCustomAttributes methods fixes this

    public override object[] GetCustomAttributes(bool inherit)
    {
    return _actionMethod.GetCustomAttributes(inherit);
    }

    public override object[] GetCustomAttributes(Type attributeType, bool inherit)
    {
    return _actionMethod.GetCustomAttributes(attributeType, inherit);
    }

  4. Nice work. MVC 4 folks should look at my tutorial Using Asynchronous Methods in ASP.NET MVC 4 http://www.asp.net/mvc/tutorials/mvc-4/using-asynchronous-methods-in-aspnet-mvc-4

  1. Pingback: Tweets that mention Asynchronous MVC using the Task Parallel Library « Cav’s Weblog -- Topsy.com

  2. Pingback: Using Task<T> in ASP.NET MVC Today – DevHawk

  3. Pingback: ASP.NET MVC 3 – Accessing Session inside Task of AsyncController « Tito's space

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: