Effectively use async/await with ASP.NET Web API

arp picture arp · Jul 2, 2015 · Viewed 185k times · Source

I am trying to make use of the async/await feature of ASP.NET in my Web API project. I am not very sure whether it will make any difference in performance of my Web API service. Please find below the workflow and sample code from my application.

Work Flow:

UI Application → Web API endpoint(controller) → Call method in Web API service layer → Call another external web service. (Here we have the DB interactions, etc.)

Controller:

public async Task<IHttpActionResult> GetCountries()
{
    var allCountrys = await CountryDataService.ReturnAllCountries();

    if (allCountrys.Success)
    {
        return Ok(allCountrys.Domain);
    }

    return InternalServerError();
}

Service Layer:

public Task<BackOfficeResponse<List<Country>>> ReturnAllCountries()
{
    var response = _service.Process<List<Country>>(BackOfficeEndpoint.CountryEndpoint, "returnCountries");

    return Task.FromResult(response);
}

I tested the above code and is working. But I am not sure whether it is the correct usage of async/await. Please share your thoughts.

Answer

Stephen Cleary picture Stephen Cleary · Jul 2, 2015

I am not very sure whether it will make any difference in performance of my API.

Bear in mind that the primary benefit of asynchronous code on the server side is scalability. It won't magically make your requests run faster. I cover several "should I use async" considerations in my article on async ASP.NET.

I think your use case (calling other APIs) is well-suited for asynchronous code, just bear in mind that "asynchronous" does not mean "faster". The best approach is to first make your UI responsive and asynchronous; this will make your app feel faster even if it's slightly slower.

As far as the code goes, this is not asynchronous:

public Task<BackOfficeResponse<List<Country>>> ReturnAllCountries()
{
  var response = _service.Process<List<Country>>(BackOfficeEndpoint.CountryEndpoint, "returnCountries");
  return Task.FromResult(response);
}

You'd need a truly asynchronous implementation to get the scalability benefits of async:

public async Task<BackOfficeResponse<List<Country>>> ReturnAllCountriesAsync()
{
  return await _service.ProcessAsync<List<Country>>(BackOfficeEndpoint.CountryEndpoint, "returnCountries");
}

Or (if your logic in this method really is just a pass-through):

public Task<BackOfficeResponse<List<Country>>> ReturnAllCountriesAsync()
{
  return _service.ProcessAsync<List<Country>>(BackOfficeEndpoint.CountryEndpoint, "returnCountries");
}

Note that it's easier to work from the "inside out" rather than the "outside in" like this. In other words, don't start with an asynchronous controller action and then force downstream methods to be asynchronous. Instead, identify the naturally asynchronous operations (calling external APIs, database queries, etc), and make those asynchronous at the lowest level first (Service.ProcessAsync). Then let the async trickle up, making your controller actions asynchronous as the last step.

And under no circumstances should you use Task.Run in this scenario.