Asynchronous Controllers in ASP.NET MVC 2

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:

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

Jeff Prosise

View Comments

  • What is the experience for the end-user as the RSS feeds is being read? What does the user see before the IndexCompleted method is called? Is anything rendered at all to the screen?

  • No. Neither async pages nor async controllers improve the user experience directly. They're all about scalability on the server.

  • Very nice Jeff. Can't wait to give it a try a little later.
    Any reason to not return the list in the View and use a strongly typed view?

  • No reason at all. I used weak binding just to keep it simple. But a strongly typed view works just as well with an async controller as it does with a synchronous controller.

  • That's cool.
    Are there 2 different thread pools for web requests and background operations ?

  • Even without peeking at the source code, I'm sure it's built on top of ThreadPool.QueueUserWorkItem just like async pages in ASP.NET. The background tasks don't require threads while they wait for I/O to complete. Async I/O generally uses I/O completion ports.

  • Good question from above, and often misunderstood.
    If you wanted to improve user experience you could use an ajax call.
    Thanks for the writeup.

  • Nice post Jeff!
    In response to @Matthew, async can help improve the user experience, but only when there are multiple operations being done synchronously that can be done in parallel. Imagine that you were actually aggregating news from three different RSS feeds. By calling out to each one of them separately, asynchronously, you could reduce the total time required for the request to something near the MAX of the three calls' response times. If all were equal to start with, the result would be a page that responds in approximately 1/3 the time. I've done this with ASP.NET async pages - I'm assuming it would work in MVC as well.

  • In this case the webservice provides asynchorneous features so IAsycController suits well here. what if I want to call another class method which is not in async pattern?
    The class must implment in async pattern?

Recent Posts

8-Step AWS to Microsoft Azure Migration Strategy

Microsoft Azure and Amazon Web Services (AWS) are two of the most popular cloud platforms.…

4 days ago

How to Navigate Azure Governance

 Cloud management is difficult to do manually, especially if you work with multiple cloud…

2 weeks ago

Why Azure’s Scalability is Your Key to Business Growth & Efficiency

Azure’s scalable infrastructure is often cited as one of the primary reasons why it's the…

4 weeks ago

Unlocking the Power of AI in your Software Development Life Cycle (SDLC)

https://www.youtube.com/watch?v=wDzCN0d8SeA Watch our "Unlocking the Power of AI in your Software Development Life Cycle (SDLC)"…

1 month ago

The Role of FinOps in Accelerating Business Innovation

FinOps is a strategic approach to managing cloud costs. It combines financial management best practices…

1 month ago

Azure Kubernetes Security Best Practices

Using Kubernetes with Azure combines the power of Kubernetes container orchestration and the cloud capabilities…

2 months ago