I’ve been working with version 2 of Microsoft’s ASP.NET MVC framework and have found a lot to like about it, particularly the new support for client-side validation based on data annotations. But the feature that excites me the most is support for asynchronous controllers and controller methods. Asynchronous pages have always been the secret to writing ASP.NET apps that scale, and now we can apply the same asynchronous magic to I/O-bound methods in our MVC controllers, too.

As an example, suppose you have a simple controller with a synchronous action method that reaches out to Abcnews.com to fetch an RSS feed containing top news stories:

public class HomeController : Controller

{

    public ActionResult Index()

    {

        RssFeed feed = new RssFeed();

        IEnumerable<SyndicationItem> items =

            feed.GetRssFeed("http://feeds.abcnews.com/abcnews/topstories");

        ViewData["SyndicationItems"] = items;

        return View();

    }

}

The Index view could then render the SyndicationItems into hyperlinks by doing this:

<% foreach (SyndicationItem item in

    (IEnumerable<SyndicationItem>)ViewData["SyndicationItems"]) { %>

    <a href="<%= item.Id %>"><%= item.Title.Text %></a><br />

<% } %>

The problem with this approach is that once the call to Abcnews.com goes out on the wire, you have no control over how long it will take to return. If the controller is synchronous, the request-processing thread must wait for the call to complete. Meanwhile, it’s not available to process other calls, and with a finite-sized CLR thread pool to draw from, you can quickly run out of threads.

Asynchronous controllers offer an elegant and (relatively) simple solution to the problem of I/O-bound threads. In MVC 2, you can rewrite the controller this way:

public class HomeController : AsyncController

{

    public void IndexAsync()

    {

        AsyncManager.OutstandingOperations.Increment();

           

        RssFeed feed = new RssFeed();

        feed.GetRssFeedAsyncCompleted += (s, e) =>

            {

                AsyncManager.Parameters["items"] = e.Items;

                AsyncManager.OutstandingOperations.Decrement();

            };

        feed.GetRssFeedAsync("http://feeds.abcnews.com/abcnews/topstories");

    }

 

    public ActionResult IndexCompleted(IEnumerable<SyndicationItem> items)

    {

        ViewData["SyndicationItems"] = items;

        return View();

    }

}

Observe that the controller class now derives from AsyncController rather than Controller. In addition, the Index action method has been split into methods named IndexAsync and IndexCompleted, which are analagous to the Begin and End methods in asynchronous pages. Logically, the controller still exposes a single action method named Index. But physically, the method implementation has been broken up using a variation on the async pattern used throughout the .NET framework.

When the Index action is invoked, the MVC framework calls IndexAsync. That method makes an asynchronous call into the model (note that it now calls GetRssFeedAsync rather than GetRssFeed) to fetch the RSS feed asynchronously. When GetRssFeedAsync completes, the controller first creates the parameter passed to IndexCompleted by adding an item named "items" to AsyncManager’s Parameters collection. Then it calls AsyncManager.OutstandingOperations.Decrement to reduce the count of "outstanding operations" (read: asynchronous tasks) from 1 to 0. When the count reaches 0, AsyncManager knows that all asynchronous tasks are complete. It responds by letting the framework know, and the framework responds by calling IndexCompleted. One aspect of this schema is that an asynchronous action method could launch several asynchronous tasks in parallel, but the action itself wouldn’t complete until all asynchronous tasks are complete. Moreover, the asynchronous tasks don’t have to know about one another. Each tasks simply does its thing, decrements the operation count when it’s done, and lets the framework do the rest.

These code samples come from a project that I created to demonstrate the basics of asynchronous MVC controllers. Here’s the application running in IE and showing the day’s top headlines from a live RSS feed:

MVC Async Demo

But rather than take my word for it, download the sample code and try it yourself!