Async controllers and actions in .NET4.5 MVC4 with C#

In the previous post we looked at the basics of async and await in a Console app. Now we’re ready to implement these new features in MVC4 to create async controllers.

The way to build asynchronous controllers has been completely changed compared to how it was done in MVC3 with the AsyncController superclass. This class and the inherent complexity in using it are gone in MVC4.

Start Visual Studio 2012 and create an MVC4 Web Application and choose the Internet template. Create a folder called “Services” in the solution. Insert two classes in this folder: DatabaseService and CalculationService. They represent long running calls to external services and have the following content:

DatabaseService.cs:

public class DatabaseService
    {
        public string GetData()
        {
            StringBuilder dataBuilder = new StringBuilder();
            dataBuilder.Append("Starting GetData on thread id ").Append(Thread.CurrentThread.ManagedThreadId)
                .Append(". ");
            Thread.Sleep(2000);
            dataBuilder.Append("Results from the database. ").Append(Environment.NewLine);
            dataBuilder.Append("Finishing GetData on thread id ").Append(Thread.CurrentThread.ManagedThreadId)
                .Append(".");
            return dataBuilder.ToString();
        }
    }

CalculationService.cs:

public class CalculationService
    {
        public string GetResult()
        {
            StringBuilder resultBuilder = new StringBuilder();
            resultBuilder.Append("Starting GetResult on thread id ").Append(Thread.CurrentThread.ManagedThreadId)
                .Append(". ");
            Thread.Sleep(2000);
            resultBuilder.Append("This is the result of a long running calculation. ");
            resultBuilder.Append("Finishing GetResult on thread id ").Append(Thread.CurrentThread.ManagedThreadId)
                .Append(".");
            return resultBuilder.ToString();
        }
    }

There should be nothing complicated in either class implementation.

Create another folder in the solution called ViewModels. Add a class called HomePageViewModel in that folder:

public class HomePageViewModel
    {
        public List<String> Messages { get; set; }

        public void AddMessage(string message)
        {
            if (Messages == null)
            {
                Messages = new List<string>();
            }
            Messages.Add(message);
        }
    }

Navigate to the Index action of the Home controller and modify it as follows:

public ActionResult Index()
        {
            DateTime startDate = DateTime.UtcNow;

            HomePageViewModel viewModel = new HomePageViewModel();
            viewModel.AddMessage(string.Concat("Starting Action on thread id ", Thread.CurrentThread.ManagedThreadId));
            CalculationService calcService = new CalculationService();
            DatabaseService dataService = new DatabaseService();

            viewModel.AddMessage(calcService.GetResult());
            viewModel.AddMessage(dataService.GetData());

            DateTime endDate = DateTime.UtcNow;
            TimeSpan diff = endDate - startDate;

            viewModel.AddMessage(string.Concat("Finishing Action on thread id ", Thread.CurrentThread.ManagedThreadId));
            viewModel.AddMessage(string.Concat("Action processing time: ", diff.TotalSeconds));
            return View(viewModel);
        }

Nothing complicated here either: we’re just adding messages to the view model, show the thread ids and measure the time it takes to complete the action.

Modify Index.cshtml of the Home view as follows:

@model Mvc4.ViewModels.HomePageViewModel
@{
    ViewBag.Title = "Home Page";
}

<ul>
    @foreach (String message in Model.Messages)
    {
        <li>@message</li>
    }
</ul>

So when you run the web page you should see an output similar to the following:

Screen with no async

It’s easy to see the following:

  • The Index() action blocks the thread when it calls CalculationService and DatabaseService
  • The total processing time took about 4 seconds in total
  • All involved methods executed on the same thread

Now our goal is to make this process more efficient: as it stands now the main thread is only sitting idle for most of the processing time. This can be a serious problem if we’re intending to build a scalable and responsive application.

We need to make a couple of changes to our code:

  • The Index() action needs to return a Task of type ActionResult and turned to an async method: we do not directly return an ActionResult but a Task that represents an ActionResult
  • Remember that if an action is of type async then it needs to have its pair ‘await’ somewhere in the method body
  • We have two long running method calls within the Index() action, so we’ll instruct MVC4 to await them
  • Since we’ll await those two method calls we need to insert async versions of DatabaseService.GetData() and CalculationService.GetResult() as well: they in turn must also return Tasks of type string instead of plain string

Using our experience from the Console app in the previous post we’ll include an async version of GetResult() in CalculationService.cs:

public async Task<String> GetResultAsync()
        {
            StringBuilder resultBuilder = new StringBuilder();
            resultBuilder.Append("Starting GetResult on thread id ").Append(Thread.CurrentThread.ManagedThreadId)
                .Append(". ");
            await Task.Delay(2000);
            resultBuilder.Append("This is the result of a long running calculation. ");
            resultBuilder.Append("Finishing GetResult on thread id ").Append(Thread.CurrentThread.ManagedThreadId)
                .Append(".");
            return resultBuilder.ToString();
        }

We’ll also insert a GetDataAsync() in DatabaseService.cs:

public async Task<String> GetDataAsync()
        {
            StringBuilder dataBuilder = new StringBuilder();
            dataBuilder.Append("Starting GetData on thread id ").Append(Thread.CurrentThread.ManagedThreadId)
                .Append(". ");
            await Task.Delay(2000);
            dataBuilder.Append("Results from the database. ").Append(Environment.NewLine);
            dataBuilder.Append("Finishing GetData on thread id ").Append(Thread.CurrentThread.ManagedThreadId)
                .Append(".");
            return dataBuilder.ToString();
        }

Update the Index action of the Home controller to call the async method versions of the services:

public async Task Index()
        {
            DateTime startDate = DateTime.UtcNow;

            HomePageViewModel viewModel = new HomePageViewModel();
            viewModel.AddMessage(string.Concat("Starting Action on thread id ", Thread.CurrentThread.ManagedThreadId));
            CalculationService calcService = new CalculationService();
            DatabaseService dataService = new DatabaseService();

            string calculationResult = await calcService.GetResultAsync();
            string databaseResult = await dataService.GetDataAsync();

            viewModel.AddMessage(calculationResult);
            viewModel.AddMessage(databaseResult);

            DateTime endDate = DateTime.UtcNow;
            TimeSpan diff = endDate - startDate;

            viewModel.AddMessage(string.Concat("Finishing Action on thread id ", Thread.CurrentThread.ManagedThreadId));
            viewModel.AddMessage(string.Concat("Action processing time: ", diff.TotalSeconds));
            return View(viewModel);
        }

Run the web app now and you may see an output similar to the following:

MVC screen with async in place

Note the following:

  • The Index action started on thread id 10
  • The same thread enters GetResultAsync
  • The main thread exits GetResultAsync at the await keyword and thread 9 takes over
  • Thread 9 enters GetDataAsync and exits at the await keyword and thread 5 takes over
  • Thread 5 finished Index()
  • The total processing time is still about 4 seconds, but remember from the previous post: if you want to couple asynchronous methods with concurrency you need to include the TPL as well. This will be resolved later in this post.

The output on your screen may be different when you run the sample. It is not guaranteed that 3 different threads will be involved throughout the lifetime of the Index() action. It depends on the availability of threads, may only be 2 in total.

If you are working with WCF services and add a service reference to your project then you’ll have the option to generate the Async() versions of the service calls automatically. Make sure to select the ‘Allow generation of asynchronous operations’ checkbox and the ‘Generate task-based operations’ radiobutton in the Service Reference Settings window.

.NET4.5 is now interspersed with built-in async versions of long running and/or remote methods. Typical examples include: HttpClient.SendAsync that returns a Task of type HttpResponseMessage, or HttpContent.ReadAsStringAsync that returns a Task of type String.

We can now introduce TPL to make the service calls run in parallel. As it stands now the Index action first waits for GetResultAsync to finish before it goes on with GetDataAsync. Ideally Index should wait for both actions to complete in parallel and not one after the other. We will basically hold a reference to the Task values returned by the services and await them both together.

Update the Index action as follows:

public async Task<ActionResult> Index()
        {
            DateTime startDate = DateTime.UtcNow;

            HomePageViewModel viewModel = new HomePageViewModel();
            viewModel.AddMessage(string.Concat("Starting Action on thread id ", Thread.CurrentThread.ManagedThreadId));
            CalculationService calcService = new CalculationService();
            DatabaseService dataService = new DatabaseService();

            Task<String> calculationResultTask = calcService.GetResultAsync();
            Task<String> databaseResultTask = dataService.GetDataAsync();

            await Task.WhenAll(calculationResultTask, databaseResultTask);

            viewModel.AddMessage(calculationResultTask.Result);
            viewModel.AddMessage(databaseResultTask.Result);

            DateTime endDate = DateTime.UtcNow;
            TimeSpan diff = endDate - startDate;

            viewModel.AddMessage(string.Concat("Finishing Action on thread id ", Thread.CurrentThread.ManagedThreadId));
            viewModel.AddMessage(string.Concat("Action processing time: ", diff.TotalSeconds));
            return View(viewModel);
        }

Note the following:

  • We do not await the two service calls one by one
  • Both of them will be awaited using Task.WhenAll
  • Task.WhenAll accepts an array of Task objects that should run in parallel
  • Task.WhenAll will block until all tasks in the array have finished
  • The await keyword will make sure that Index will wait upon all tasks to complete in the array
  • To retrieve the returned value from the service calls just use the Result property of the Task object: this will be populated if the Task has a return value i.e. it is a Task of some type

When you run the updated Index page you may see something like this:

Screen with async and TPL

Again, your results will almost certainly differ. Which thread is allocated to which task is up to the thread scheduler. Refresh the page a couple of times to see some different results.

In the next post we’ll wrap up the discussion with some miscellaneous issues such as timeouts or exception handling in conjunction with asynchronous controllers.

View the list of MVC and Web API related posts here.

Advertisements

About Andras Nemes
I'm a .NET/Java developer living and working in Stockholm, Sweden.

16 Responses to Async controllers and actions in .NET4.5 MVC4 with C#

  1. Long says:

    It’s a very nice tutorial. Thank you. Can I ask you something? I want to write a action that call a async method. All I want is the async method still run even when the action return result.It mean action don’t have to wait the async method is over. Could it be possible?

    • Andras Nemes says:

      Hello, so you’d like to start a Task on a separate thread and never read its result? You don’t need await-async for that, just start the action on a separate thread.
      //Andras

      • Long says:

        Thank you for your reply. Can I explain it more clearly?
        The module is to request the resource from another system.However, it limit elements to request in fews second, so I have to delay task in fews second then start it again.
        But I don’t want user wait until the task is finish. I want the action finish and show the success message for user so they can do another job, while the async module still run.
        Could it be possible?
        P/S: sorry for my bad english.

      • Andras Nemes says:

        A couple more questions on this bit:

        “I want the action finish and show the success message for user so they can do another job, while the async module still run.”

        a.) Why would you show a success message before the async module has completed?
        b.) would you like to show the result of the async operation on the screen or not?

        If I understood your description correctly then you’d like to achieve the following:

        1. User requests a URL in the browser
        2. The URL needs a resource that takes time to collect
        3. The browser shouldn’t wait for the result but show some OK message
        4. When the long-running process is complete then you’d like to show the result on the screen

        Is this correct?
        //Andras

      • Long says:

        Yeah, something like that :

        User click button to get resource to save it to database.

        The resource take time to collect and save to database.

        The browse shouldn’t wait for result but show some OK message.

        When the long-running process is complete, browse can show message to notify the process is complete.(may be not, the notify message is not necessary)

        Thanks.

      • Andras Nemes says:

        You cannot achieve that just with await and async. After the page has refreshed the connection to the server is closed. If you want to show a message on the screen AFTER the page has been refreshed then you’ll need to do some more research and work. Some suggestions:

        • The page refreshes and then automatically polls the status of the background job via automatic AJAX page refreshes
        • Same as above but with an elegant WebSockets solution where the server keeps the connection open and sends message updates to the client
        • The easiest solution: put a Refresh button on the screen and let the user check the updates

        In all cases you’ll need to store the state of the job somewhere: in a database, on disk, whatever, so that the latest status of the job can be checked.
        //Andras

      • Long says:

        Thank for your suggestions.
        Actually, I don’t have to show message success to user when the long-time process is completed.
        I just want to achieve process like that:

        The user click button to call MVC4 action.

        The action call an async method in it’s body then immidiately return result or redirect to another page without wait async method to complete.

        The async method is still running without any problem.

        Could I achieve it with async – await? I have write module when I read this tutorial. But it’s seem the action always wait for async method complete before return result.

        Thanks.
        Long

      • Andras Nemes says:

        As I said in the first comment: you don’t need async-await for that. If you use async-await then you’ll wait upon the results by default. Just put the long running action in a separate Task, but don’t put the await keyword in front of it. I guess you know how to start a Task on a separate thread, but if not, then you can have a look at this page: TPL.
        //Andras

      • Long says:

        Thank for your instruction. It is reallly helpful to me.

        Iam sorry for bother you so many time.

        Thanks.
        Long

      • Andras Nemes says:

        You’re welcome and you didn’t bother me at all. //Andras

  2. Long says:

    I have module which was write in normal way. Then I change to this way with async – await and Task.WhenAll. I expect it run with multi-thread to get more performance with less time spend.
    But it’s seem it did not use more CPU than before.
    So Is this run with multi-thread?
    Thanks
    Long

  3. TryingHard says:

    I’m really trying to understand the new async/await/Task stuff, but even though it is certainly simpler than the async stuff of before, I’m still having some difficulties. If I understand this article, and a pluralsight course on async/await within ASP.NET MVC apps, correctly, it seems to me that if there is only one async operation executed from within an MVC controller’s action method, there will be no time savings from the standpoint of the UI response. It is only when two or more async operations are executed, IN PARALLEL, that there will be any time savings compared to all those two more more operations happening synchronously/serially. Would this be a correct statement?

    • Andras Nemes says:

      Hello TryingHard,

      The Web UI won’t be more responsive through asynchronous controllers, but the available threads will be better utilised. A single async call to a controller won’t make your website faster for that call in isolation.
      Yes, if you can parallelise your code then you’ll save time by executing two methods in parallel but having async methods alone won’t execute your code in parallel.

      As far as the GUI is concerned the advantages of await-async are more visible in desktop apps, such as Windows Store apps. With await-async in that scenario you can have a highly responsive UI with e.g. one part of the UI executing a file upload operation while another part of the UI playing music without freezing the UI at all.

      //Andras

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 )

Google photo

You are commenting using your Google 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 )

Connecting to %s

ultimatemindsettoday

A great WordPress.com site

Elliot Balynn's Blog

A directory of wonderful thoughts

Softwarearchitektur in der Praxis

Wissenswertes zu Webentwicklung, Domain-Driven Design und Microservices

Technology Talks

on Microsoft technologies, Web, Android and others

Software Engineering

Web development

Disparate Opinions

Various tidbits

chsakell's Blog

WEB APPLICATION DEVELOPMENT TUTORIALS WITH OPEN-SOURCE PROJECTS

Guru N Guns's

OneSolution To dOTnET.

Johnny Zraiby

Measuring programming progress by lines of code is like measuring aircraft building progress by weight.

%d bloggers like this: