Filtered by Tag (tpl)
How to most efficiently deal with asynchrony inside ASP.NET Web API HTTP Message Handlers with TaskHelpers NuGet package or C# 5.0 asynchronous language features
@ 09-08-2012
by Tugberk Ugurlu


ASP.NET Web API has a concept of Message Handlers for processing HTTP messages on both the client and server. If we take look under the hood inside the framework, we will see that Initialize method of the HttpServer instance is invoking CreatePipeline method of System.Net.Http.HttpClientFactory’s. CreatePipeline method accepts two parameters: HttpMessageHandler and IEnumerable<DelegatingHandler>.HttpServer.Initialize method is passing System.Web.Http.Dispatcher.HttpControllerDispatcher for HttpMessageHandlerparameter as the last HttpMessageHandler inside the chain and HttpConfiguration.MessageHandlers for IEnumerable<DelegatingHandler> parameter.

What happens inside the CreatePipeline method is very clever IMO:

public static HttpMessageHandler CreatePipeline(
    HttpMessageHandler innerHandler, 
    IEnumerable<DelegatingHandler> handlers) {
    
    if (innerHandler == null)
    {
        throw Error.ArgumentNull("innerHandler");
    }

    if (handlers == null)
    {
        return innerHandler;
    }

    // Wire handlers up in reverse order starting with the inner handler
    HttpMessageHandler pipeline = innerHandler;
    IEnumerable<DelegatingHandler> reversedHandlers = handlers.Reverse();
    foreach (DelegatingHandler handler in reversedHandlers)
    {
        if (handler == null)
        {
            throw Error.Argument("handlers", 
                Properties.Resources.DelegatingHandlerArrayContainsNullItem, 
                typeof(DelegatingHandler).Name);
        }

        if (handler.InnerHandler != null)
        {
            throw Error.Argument("handlers", 
                Properties.Resources
                .DelegatingHandlerArrayHasNonNullInnerHandler, 
                typeof(DelegatingHandler).Name, 
                "InnerHandler", 
                handler.GetType().Name);
        }

        handler.InnerHandler = pipeline;
        pipeline = handler;
    }

    return pipeline;
}

As you can see, the message handler order is reversed and the Matryoshka Doll is created but be careful here: it is ensured that HttpControllerDispatcher is the last message handler to run inside the chain. Also there is a bit of a misunderstanding about message handlers, IMHO, about calling the handler again on the way back to the client. This is actually not quite correct because the message handler’s SendAsync method itself won’t be called twice, the continuation delegate that you will chain onto SendAsync method will be invoked on the way back to client with the generated response message that you can play with.

For example, let's assume that the following two are the message handler:

public class XMagicMessageHandler : DelegatingHandler {

    protected override Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, 
        CancellationToken cancellationToken) {
            
        //Play with the request here

        return base.SendAsync(request, cancellationToken)
            .ContinueWith(task => {

                //inspect the generated response
                var response = task.Result;

                //Add the X-Magic header
                response.Headers.Add("X-Magic", "ThisIsMagic");

                return response;
        });
    }
}

Besides this I have a tiny controller which serves cars list if the request is a GET request and I registered this message handler through the GlobalConfiguration object as below:

protected void Application_Start(object sender, EventArgs e) {

    var config = GlobalConfiguration.Configuration;
    config.Routes.MapHttpRoute("DefaultHttpRoute", "api/{controller}");
    config.MessageHandlers.Add(new XMagicMessageHandler());
    config.MessageHandlers.Add(new SecondMessageHandler());
}

When we send a request to /api/cars, the result should be similar to below one:

image

Worked great! But, there is very nasty bug inside our message handler? Can you spot it? OK, I’ll wait a minute for you...

Did u spot it? No! OK, no harm there. Let’s try to understand what it was. I added another message handler and that message handler runs after the XMagicMessageHandler but on the way back, it runs first. The continuation delegate for the message handler will throw DivideByZeroException exception on purpose.

public class SecondMessageHandler : DelegatingHandler {

    protected override Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request,
        CancellationToken cancellationToken) {

        return base.SendAsync(request, cancellationToken).ContinueWith(task => {

            throw new DivideByZeroException();

            return new HttpResponseMessage();
        });
    }
}

Now let’s put a breakpoint inside the continuation method of our XMagicMessageHandler and see what we got.

image

As you might expect, the Result property of the task doesn’t contain the response message generated by the controller. When we try to reach out to the Result property, the exception thrown by the SecondMessageHandler will be re-thrown here again and more importantly, IMO, your code doesn’t do what it is supposed to do. So, how do we get around this? Surely, you can put a try/catch block around the task.Result but that'd be a lame solution. The answer depends on what version of .NET Framework are you running on.

If you are running on .NET v4.0, things are a bit harder for you as you need to deal with TPL face to face. Thanks to ASP.NET team, it is now easier with TaskHelpers NuGet package. The TaskHelpers is actively being used by some major ASP.NET Frameworks internally at Microsoft such as ASP.NET Web API which embraces TPL from top to bottom all the way through the pipeline.

PM> Install-Package TaskHelpers.Sources

If you would like to learn more about TaskHelpers class and how it helps you, @bradwilson has a nice blog posts series on this topic and I listed the links to those posts under resources section at the bottom of this post. After installing the TaskHelpers package from NuGet, we need to fix the bugs inside our message handlers.

public class SecondMessageHandler : DelegatingHandler {

    protected override Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request,
        CancellationToken cancellationToken) {

        return base.SendAsync(request, cancellationToken).Then(response => {

            int left = 10, right = 0;
            var result = left / right;

            return response;

        }).Catch<HttpResponseMessage>(info => {

            var cacthResult = 
                new CatchInfoBase<Task<HttpResponseMessage>>.CatchResult();

            cacthResult.Task = TaskHelpers.FromResult(
                request.CreateErrorResponse(
                    HttpStatusCode.InternalServerError, info.Exception));

            return cacthResult;
        });
    }
}

So, what did we actually change? We now use Then method from the TaskHelpers package instead of directly using ContinueWith method. What Then method does is that it only runs the continuation if the Task’s status is RanToCompletion. So, if the base.SendAsync method here returns a faulted or cancelled task, the continuation delegate that we pass into the Then method won’t be invoked.

Secondly, we chain another method called Catch which only runs the continuation delegate if the task’s status is faulted. If the status is cancelled, the Catch method will return back a Task whose status is set to cancelled. Inside the continuation delegate, we construct a new HttpResponseMessage through CreateErrorResponse extension method for HttpRequestMessage by passing the response status and the exception. The exception details are only sent over the wire if the following conditions are met:

  • If you set GlobalConfiguration.Configuration.IncludeErrorDetailPolicy to IncludeErrorDetailPolicy.Always.
  • if you set GlobalConfiguration.Configuration.IncludeErrorDetailPolicy to IncludeErrorDetailPolicy.LocalOnly and you run your application locally.
  • If you set GlobalConfiguration.Configuration.IncludeErrorDetailPolicy to IncludeErrorDetailPolicy.Default and your host environment’s error policy allows you to expose error details (for ASP.NET hosting, customErrors element in the Web.config file).

The XMagicMessageHandler has been changed nearly the same way as SecondMessageHandler.

public class XMagicMessageHandler : DelegatingHandler {

    protected override Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, 
        CancellationToken cancellationToken) {
            
        //Play with the request here

        return base.SendAsync(request, cancellationToken)
            .Then(response => {

                //Add the X-Magic header
                response.Headers.Add("X-Magic", "ThisIsMagic");
                return response;

            }).Catch<HttpResponseMessage>(info => {

                var cacthResult = 
                    new CatchInfoBase<Task<HttpResponseMessage>>.CatchResult();

                cacthResult.Task = TaskHelpers.FromResult(
                    request.CreateErrorResponse(
                        HttpStatusCode.InternalServerError, info.Exception));

                return cacthResult;
            });
    }
}

As I am running the application locally, I should see the error details if I send a request.

image

If I were to send a request with application/xml Accept header, I would get the error response back in xml format.

image

If you are on .NET v4.5, you will get lots of the things, which we’ve just done, out of the box. The Then and Catch extensions method from TaskHelpers package sort of mimic the new async/await compiler features some parts of the new async/await compiler features. So, the .NET v4.5 version of our message handlers look as below:

public class SecondMessageHandler : DelegatingHandler {

    protected override async Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request,
        CancellationToken cancellationToken) {

        try {

            int left = 10, right = 0;
            var result = left / right;

            var response = await base.SendAsync(request, cancellationToken);
            return response;
        }
        catch (Exception ex) {

            return request.CreateErrorResponse(
                HttpStatusCode.InternalServerError, ex);
        }
    }
}
public class XMagicMessageHandler : DelegatingHandler {

    protected override async Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, 
        CancellationToken cancellationToken) {

        try {

            var response = await base.SendAsync(request, cancellationToken);
            response.Headers.Add("X-Magic", "ThisIsMagic");
            return response;
        }
        catch (Exception ex) {

            return request.CreateErrorResponse(
                HttpStatusCode.InternalServerError, ex);
        }
    }
}

I would like to clear out that the code that we wrote for .NET v4.0 would work just fine under .NET v4.5 but as you can see we get rid of all the noise here and have a nice and more readable code. The functionality also stays the same.

image

As expected stack trace is different but the result is the same. No matter which way you choose, just choose one. Don’t leave things to chance with message handlers!

Resources

Let's see how we can end up with a deadlock using the C# 5.0 asynchronous language features (AKA async/await) in our ASP.NET applications and how to prevent these kinds of scenarios.
@ 06-02-2012
by Tugberk Ugurlu


shoot_yourself_in_the_footI just finished The zen of async: Best practices for best performance talk of Stephen Toub’s on //Build and learnt how easy is to end up with a deadlock while writing asynchronous code with new C# 5.0 language features (AKA async/await). Here is the quick background first which I was aware of before I watched this talk. The talk that Toub has given was mostly about how you create better reusable libraries with asynchronous language features but there are lots of great information concerning application level code.

When you are awaiting on a method with await keyword, compiler generates bunch of code in behalf of you. One of the purposes of this action is to handle synchronization with the UI thread. The key component of this feature is the SynchronizationContext.Current which gets the synchronization context for the current thread. SynchronizationContext.Current is populated depending on the environment you are in. For example, if you are on a WPF application, SynchronizationContext.Current will be a type of DispatcherSynchronizationContext. In an ASP.NET application, this will be an instance of AspNetSynchronizationContext which is not publicly available and not meant for external consumptions. SynchronizationContext has virtual Post method which dispatches an asynchronous message to a synchronization context when overridden in a derived class. When you give this method a delegate, this delegate will be marshaled back to the UI thread and invoked on that UI thread.

The GetAwaiter method (yes, "waiter" joke is proper here) of Task looks up for SynchronizationContext.Current. If current synchronization context is not null, the continuation that gets passed to that awaiter will get posted back to that synchronization context. This feature is a great feature when we are writing application level code but it turns out that if we are not careful enough, we might end up with a deadlock because of this. Here is how:

build_task

This is a part of the slide from that talk and shows how we can end up with a deadlock. Task has a method named Wait which hangs on the task till it completes. This method is an evil method in my opinion but I am sure that there is a good reason why it is provided. You never ever want to use this method. But, assume that you did use it on a method which returns a Task or Task<T> for some T and uses await inside it to await another task, you will end up with a deadlock if you are not careful. The picture explains how that can happen but let’s recap with an example.

public class HomeController : Controller {

    public ActionResult Index() {
            
        doWorkAsync().Wait();
        return View();
    }

    private async Task doWorkAsync() {

        await Task.Delay(500);
    }

}

The above code has an ASP.NET MVC 4 asynchronous action method (I am using .NET 4.5 here) which consumes the private doWorkAsync method. I used ASP.NET MVC here but everything applies for ASP.NET Web API asynchronous action methods as well. Inside the doWorkAsync method, we used Delay method of Task to demonstrate but this could be any method which returns a Task or Task<T>. inside the Index action method, we invoke the Wait method on the task which we got from doWorkAsync method to ensure that we won’t go further unless the operation completes and the interesting part happens right here. At that point we block the UI thread at the same time. When eventually the Task.Delay method completes in the threadpool, it is going to invoke the continuation to post back to the UI thread because SynchronizationContext.Current is available and captured. But there is a problem here: the UI thread is blocked. Say hello to our deadlock!

When you run the application, you will see that the web page will never come back. Solution to this problem is so simple: don’t use the Wait method. The code you should be writing should be as follows:

public class HomeController : Controller {

    public async Task<ActionResult> Index() {
        
        await doWorkAsync();
        return View();
    }

    private async Task doWorkAsync() {

        var task = Task.Delay(500);
    }
}

But if you have to use it for some weird reasons, there is another method named ConfigureAwait on Task which you can configure not to use the captured context.

public class HomeController : Controller {

    public ActionResult Index() {
        
        doWorkAsync().Wait();
        return View();
    }

    private async Task doWorkAsync() {

        var task = Task.Delay(500);
        await task.ConfigureAwait(continueOnCapturedContext: false);
    }
}

Very nice little and tricky information but a lifesaver.

Resources

Asynchronous Database Calls With Task-based Asynchronous Programming Model (TAP) in ASP.NET MVC 4 and its performance impacts.
@ 04-09-2012
by Tugberk Ugurlu


You have probably seen some people who are against asynchronous database calls in ASP.NET web applications so far. They mostly right but there are still some cases that processing database queries asynchronous has very important impact on.

If you are unfamiliar with asynchronous programming model on ASP.NET MVC 4, you might want to read one of my previous posts: Asynchronous Programming in C# 5.0 and ASP.NET MVC Web Applications.

Update on the 11th of April, 2012:

@BradWilson has just started a new blog post series on using Task Parallel Library when writing server applications, especially ASP.NET MVC and ASP.NET Web API applications. You should certainly check them out: Task Parallel Library and Servers, Part 1: Introduction

Update on the 1st of July, 2012:

@RickAndMSFT has a new tutorial on Using Asynchronous Methods in ASP.NET MVC 4. Definitely check that out.

One of the reasons why asynchronous programming is not recommended for database calls is that it is extremely hard to get it right, even if we adopt Task-based Asynchronous Programming in .NET 4.0. But, with the new async / await features of C# 5.0, it is easier and still complex at the same time.

When you call a method which returns Task or Task<T> for some T and await on that, the compiler does the heavy lifting by assigning continuations, handling exceptions and so on. Because of this fact, it adds a little overhead and you might notice this when you are dealing with short running operations. At that point, asynchrony will do more harm than good to your application.

Let’s assume we have a SQL Server database out there somewhere and we want to query against that database in order to get the cars list that we have. I have created a class which will do the query operations and hand us the results as C# CLR objects.

public class GalleryContext : IGalleryContext {

    private readonly string selectStatement = "SELECT * FROM Cars";

    public IEnumerable<Car> GetCars() {

        var connectionString = 
            ConfigurationManager.ConnectionStrings["CarGalleryConnStr"].ConnectionString;

        using (var conn = new SqlConnection(connectionString)) {
            using (var cmd = new SqlCommand()) {

                cmd.Connection = conn;
                cmd.CommandText = selectStatement;
                cmd.CommandType = CommandType.Text;

                conn.Open();

                using (var reader = cmd.ExecuteReader()) {

                    return reader.Select(r => carBuilder(r)).ToList();
                }
            }
        }
    }

    public async Task<IEnumerable<Car>> GetCarsAsync() {

        var connectionString = 
            ConfigurationManager.ConnectionStrings["CarGalleryConnStr"].ConnectionString;
            
        var asyncConnectionString = new SqlConnectionStringBuilder(connectionString) {
            AsynchronousProcessing = true
        }.ToString();

        using (var conn = new SqlConnection(asyncConnectionString)) {
            using (var cmd = new SqlCommand()) {

                cmd.Connection = conn;
                cmd.CommandText = selectStatement;
                cmd.CommandType = CommandType.Text;

                conn.Open();

                using (var reader = await cmd.ExecuteReaderAsync()) {

                    return reader.Select(r => carBuilder(r)).ToList();
                }
            }
        }
    }

    //private helpers
    private Car carBuilder(SqlDataReader reader) {

        return new Car {

            Id = int.Parse(reader["Id"].ToString()),
            Make = reader["Make"] is DBNull ? null : reader["Make"].ToString(),
            Model = reader["Model"] is DBNull ? null : reader["Model"].ToString(),
            Year = int.Parse(reader["Year"].ToString()),
            Doors = int.Parse(reader["Doors"].ToString()),
            Colour = reader["Colour"] is DBNull ? null : reader["Colour"].ToString(),
            Price = float.Parse(reader["Price"].ToString()),
            Mileage = int.Parse(reader["Mileage"].ToString())
        };
    }
}

You might notice that I used a Select method on SqlDataReader which does not exist. It is a small extension method which makes it look prettier.

public static class Extensions {

    public static IEnumerable<T> Select<T>(
        this SqlDataReader reader, Func<SqlDataReader, T> projection) {

        while (reader.Read()) {
            yield return projection(reader);
        }
    }
}

As you can see, it has two public methods to query the database which does the same thing but one of them doing it as synchronously and the other one as asynchronously.

Inside the GetCarsAsync method, you can see that we append the AsynchronousProcessing property and set it to true in order to run the operation asynchronously. Otherwise, no matter how you implement it, your query will be processed synchronously.

When you look behind the curtain, you will notice that ExecuteReaderAsync method is really using the old Asynchronous Programming Model (APM) under the covers.

public static Task<SqlDataReader> ExecuteReaderAsync(this SqlCommand source)
{
    return Task<SqlDataReader>.Factory.FromAsync(
        new Func<AsyncCallback, object, IAsyncResult>(source.BeginExecuteReader), 
        new Func<IAsyncResult, SqlDataReader>(source.EndExecuteReader), 
        null
    );
}

When we try to consume these methods inside our controller, we will have the following implementation.

public class HomeController : Controller {

    private readonly GalleryContext ctx = new GalleryContext();

    public ViewResult Index() {

        return View(ctx.GetCars());
    }

    public async Task<ViewResult> IndexAsync() {

        //workaround: http://aspnetwebstack.codeplex.com/workitem/22
        await TaskEx.Yield();

        return View("Index", await ctx.GetCarsAsync());
    }
}

Now, when we hit /home/Index, we will be querying our database as synchronously. If we navigate to /home/IndexAsync, we will be doing the same thing but asynchronously this time. Let’s do a little benchmarking with Apache HTTP server benchmarking tool.

First, we will simulate 50 concurrent requests on synchronously running wev page:

ab_syncdb_short_1

Let’s do the same thing for asynchronous one:

ab_asyncdb_short_1

Did you notice? We have nearly got the same result. In fact, you will see that synchronous version of the operation completes faster than the asynchronous one at some points. The reason is that the SQL query takes small amount of time (approx. 8ms) here to complete.

Let’s take another scenario. Now, we will try to get the same data through a Stored Procedure but this time, the database call will be slow (approx. 1 second). Here are two methods which will nearly the same as others:

private readonly string spName = "sp$GetCars";

public IEnumerable<Car> GetCarsViaSP() {

    var connectionString = ConfigurationManager.ConnectionStrings["CarGalleryConnStr"].ConnectionString;

    using (var conn = new SqlConnection(connectionString)) {
        using (var cmd = new SqlCommand()) {

            cmd.Connection = conn;
            cmd.CommandText = spName;
            cmd.CommandType = CommandType.StoredProcedure;

            conn.Open();

            using (var reader = cmd.ExecuteReader()) {

                return reader.Select(r => carBuilder(r)).ToList();
            }
        }
    }
}

public async Task<IEnumerable<Car>> GetCarsViaSPAsync() {

    var connectionString = ConfigurationManager.ConnectionStrings["CarGalleryConnStr"].ConnectionString;
    var asyncConnectionString = new SqlConnectionStringBuilder(connectionString) {
        AsynchronousProcessing = true
    }.ToString();

    using (var conn = new SqlConnection(asyncConnectionString)) {
        using (var cmd = new SqlCommand()) {

            cmd.Connection = conn;
            cmd.CommandText = spName;
            cmd.CommandType = CommandType.StoredProcedure;

            conn.Open();

            using (var reader = await cmd.ExecuteReaderAsync()) {

                return reader.Select(r => carBuilder(r)).ToList();
            }
        }
    }
}

I was able to make the SQL query long running by waiting inside the stored procedure for 1 second:

ALTER PROCEDURE dbo.sp$GetCars
AS

-- wait for 1 second
WAITFOR DELAY '00:00:01';

SELECT * FROM Cars;

Controller actions are nearly the same as before:

public class HomeController : Controller {

    private readonly GalleryContext ctx = new GalleryContext();

    public ViewResult IndexSP() {

        return View("Index", ctx.GetCarsViaSP());
    }

    public async Task<ViewResult> IndexSPAsync() {

        //workaround: http://aspnetwebstack.codeplex.com/workitem/22
        await TaskEx.Yield();

        return View("Index", await ctx.GetCarsViaSPAsync());
    }
}

Let’s simulate 50 concurrent requests on synchronously running web page first:

ab_syncdb_long_1

Ok, that’s apparently not good. As you might see, some requests take more than 8 seconds to complete which is very bad for a web page. Remember, the database call takes approx. 1 second to complete and under a particular number of concurrent requests, we experience a very serious bottleneck here.

Let’s have a look at the asynchronous implementation and see the difference:

ab_asyncdb_long_1

Approximately 1 second to complete for each request, pretty impressive compared to synchronous implementation.

Asynchronous database calls are not as straight forward as other types of asynchronous operations but sometimes it will gain so much more responsiveness to our applications. We just need to get it right and implement them properly.

I'm trying to show you what new C# 5.0 can bring us in terms of asynchronous programming with await keyword. Especially on ASP.NET MVC 4 Web Applications.
@ 02-26-2012
by Tugberk Ugurlu


I have been playing with Visual Studio Async CTP for a while now and I had enough idea on how it works and how I can use it for my benefit and that means that I am able to blog about it.

On most of the softwares we build, the main problem occurs on long running operations. If a user starts a long running operation, that operation blocks the main thread till it completes. Doing so will result pretty unusable applications and unhappy customers for your business. I am sure that we all have used that kind of applications in our lives.

Asynchronous programming model is not a new paradigm and it has been available on .NET since v1.0. I wasn’t very interested in programming 3 years ago so this concept is fairly new to me even now. The concept has evolved a lot on .NET in terms of asynchronous programming as far as I read and I think it has reached its best stage.

On the other hand, you can get confused about asynchronous programming pretty much easily as I did several times. I went back and forth about how it works and where to use it. As a result, I believe I finally got it right. On the other hand, It is much easier to get confused if you’d like to leverage asynchrony on web applications because you do not have a UI thread to flush your results out immediately. Asynchronous or not, the user has to wait the amount of time operation takes to complete. This makes asynchronous programming undesirable for web applications but if you think that way, as I did, you are missing the point.

If you start an operation synchronously and it takes long time, you have no option rather than blocking that thread. If you do the same operation asynchronously, what happens is that you start an operation and come back later when it finishes without waiting it to be finished. Between that duration, your thread is free and can do other stuff as well. Most of the confusion happens here I think. Creating additional threads is not cheap and may cause issues but it is all in past I think. .NET 4.0 has increases the number of thread limits very dramatically. It is still not good to walk around and create threads but it is good not to worry about them. There are couple of debates going around about asynchronous programming (or should I have said went around?):

I am not sure what is the real answer of those questions. But here are two examples:

  • Windows Runtime (WinRT) is designed to be async friendly. Anything longer than 40ms (operations related to network, file system. Mainly I/O bound operations), that is async.
  • ASP.NET Web API introduced a new way of exposing your data to the World with different formats. One thing to notice is that there is nearly no synchronous method on their API. Not metaphorically, literally there is no synchronous versions of the some methods.

That’s being said, I think we have a point here that we should take advantage of asynchrony one way or another in our applications. So, what is the problem? The problem is the way how async programming model works. As Anders Hejlsberg always says, that programing model turns our code inside out. Not to mention that it is extremely hard to do nested async operations and exception handling.

Visual Studio Async CTP and How It Works

In C# 5.0, we will have a new asynchronous programming model which looks a lot like synchronous. On the other hand, C# team has released a CTP version of those features and it has a go-live license. Here is quote from the AsyncCTP spec:

Asynchronous functions is a new feature in C# which provides an easy means for expressing asynchronous operations. Inside asynchronous functions await expressions can await ongoing tasks, which causes the rest of the execution of the asynchronous function to be transparently signed up as a continuation of the awaited task. In other words, it becomes the job of the programming language, not the programmer, to express and sign up continuations. As a result, asynchronous code can retain its logical structure.

An asynchronous function is a method or anonymous function which is marked with the async modifier. An asynchronous function can either return Task or Task<T> for some T and both of them can be awaited. Those kind of functions can also return void but it cannot be awaited. On the other hand, you can await any type if that type satisfies a certain pattern.

ASP.NET MVC 4 and C# Async Features

Benefiting from asynchrony in a right way on ASP.NET MVC applications can result huge positive performance impact. Believe it or not it’s true. I’ll show you how.

So why don't we use it much? Because it is hard and error prone. In the long run, it is hard to maintain the application as well. But with new asynchronous programming model, it is about to change.

In ASP.NET MVC 4, asynchronous programming model has been changed a lot as well. As you probably know, in ASP.NET MVC 3, our controller has to be derived from AsyncController and must satisfy a certain pattern to work with. You can see the Using an Asynchronous Controller in ASP.NET MVC article if you would like to see how it works.

In ASP.NET MVC 4, we do not need AsyncController to leverage asynchrony in our applications. Our controller actions can be marked with async keyword and return Task or Task<T> where the T is usually the type of ActionResult.

I put together a sample application which does the same thing both asynchronously and synchronously. I also did a load test and the end result was shocking. Let’s see what the code looks like:

Firstly, I created a simple REST service endpoint with new REST hotness of .NET: ASP.NET Web API. I have a simple model and collection which I store it in memory. This would be normally a database instead of memory.

public class Car {

    public string Make;
    public string Model;
    public int Year;
    public int Doors;
    public string Colour;
    public float Price;
    public int Mileage;
}

public class CarService {

    public List<Car> GetCars() {

        List<Car> Cars = new List<Car> {

            new Car{Make="Audi",Model="A4",Year=1995,Doors=4,Colour="Red",Price=2995f,Mileage=122458},
            new Car{Make="Ford",Model="Focus",Year=2002,Doors=5,Colour="Black",Price=3250f,Mileage=68500},
            new Car{Make="BMW",Model="5 Series",Year=2006,Doors=4,Colour="Grey",Price=24950f,Mileage=19500}
            //This keeps going like that
        };

        return Cars;
    }
}

And here is my Web API:

public class CarsController : ApiController { 

    public IEnumerable<Car> Get() {

        var service = new CarService();

        return service.GetCars();
    }
}

I have my service now. In my web application, I will get the data from this service and display on the web page. To do that, I created a service class which gets the data from that endpoint and deserialize the string into an object. I used the new HttpClient for asynchronous version of GetCars operation and WebClient for synchronous version of it. I also used Json.NET for working with JSON payload.

public class CarRESTService {

    readonly string uri = "http://localhost:2236/api/cars";

    public List<Car> GetCars() { 

        using (WebClient webClient = new WebClient()) {
            
            return JsonConvert.DeserializeObject<List<Car>>(
                webClient.DownloadString(uri)
            );
        }
    }

    public async Task<List<Car>> GetCarsAsync() {

        using (HttpClient httpClient = new HttpClient()) {
            
            return JsonConvert.DeserializeObject<List<Car>>(
                await httpClient.GetStringAsync(uri)    
            );
        }
    }
}

Above GetCars method is very boring as you see. Nothing to talk about. The real deal is in the second method which is GetCarsAsync:

  • The method is marked with async keyword which indicates that the method has some asynchronous code.
  • We used await keyword before HttpClient.GetStringAsync method which returns Task<string>. But notice here that we use it as string. The await keyword enables that.

Lastly, here is my controller:

public class HomeController : Controller {

    private CarRESTService service = new CarRESTService();

    public async Task<ActionResult> Index() {

        return View("index",
            await service.GetCarsAsync()
        );
    }

    public ActionResult IndexSync() {

        return View("index",
            service.GetCars()
        );
    }
}

We have two actions here, one is Index which is an asynchronous function and returns Task<ActionResult> and the second one is IndexSync which is a typical ASP.NET MVC controller action. When we navigate to /home/index and /home/indexsync, we cannot really see the difference. It takes approx. the same time.

In order to measure the difference, I configured a load test with Visual Studio 2010 Ultimate Load Testing features. I hit the two pages for 2 minutes. I started with 50 users and it incremented by 20 users per 5 seconds and max user limit was 500. The result was really shocking in terms of page response time.

asyncFTW

While average response time for the synchronous one is about 11.2 seconds, it is 3.65 for asynchronous one. I think the difference is pretty compelling and overwhelming.

From now on, I am adopting the Windows approach: "Anything longer than 40ms (operations related to network, file system. Mainly I/O bound operations), that is async!"

If you believe that some of the information here is wrong or misleading, please make me suffer and post a comment which would bring me down. Also, do the same if you have any additional information that is worth mentioning on this post but I missed.

Resources



Hi 🙋🏻‍♂️ I'm Tugberk Ugurlu.
Coder 👨🏻‍💻, Speaker 🗣, Author 📚, Microsoft MVP 🕸, Blogger 💻, Software Engineering at Deliveroo 🍕🍜🌯, F1 fan 🏎🚀, Loves travelling 🛫🛬
Lives in Cambridge, UK 🏡