2
votes

I have come across a small issue, I have written a asp.net web api service and I automatically added "async" on the controller, of course this now requires that I use await.

The problem I have is that my controller is connected to a service layer and this service layer then connects to a data layer - this data layer is the layer that is calling an ASYNC version of GET to make an asynchronous call to another REST service - so this is easy, I can convert my data layer method to have an "async" keyword and i will place a await on the HttpClient GetSync call.

but my service layer to support async / await I need to change the return type to Task but the service layer really isn't doing anything ASYNC its calling to the data layer that is actually calling HttpClient async methods.

So I have 2 options considering I have the following layer structure.

ASP.NET Web Api >> Service Layer >> Data Layer

Only have async and await used in the Data Layer and be done with it.

Or place async and await on the controller and the method from the service layer, this requires refactoring as I need to return Task.

I suppose where I am not understanding it fully, technically the only blocking calls would be in the data layer, so to have all threads returned the thread pool for use I should only be concerned with placing async and await on the data layer ?

When would i need to use pattern on the controllers ???

Look forward to any help

2

2 Answers

6
votes

I automatically added "async" on the controller, of course this now requires that I use await.

That is backwards. You should only add async to a method if you need to use await.

Only have async and await used in the Data Layer and be done with it.

You shouldn't wrap asynchronous methods within synchronous APIs. This is especially true in server code, because then you lose all the benefits of async.

Or place async and await on the controller and the method from the service layer, this requires refactoring as I need to return Task.

This is the correct approach. Follow these guidelines:

  1. Do not block on async code. E.g., do not call Task.Wait or Task<T>.Result. I describe on my blog how this can cause deadlocks, but even if you hack it to work reliably, you'll still lose all the benefits of async on the server side.
  2. Avoid async void. Especially on the server side.
  3. Instead of Wait or Result, use await. This means that the awaiting method will have to be async Task or async Task<T>, which means that methods that call the awaiting method will also have to await. So async will "grow" through your code. This is normal and correct.

You may find my article on Best Practices in Asynchronous Programming helpful.

1
votes

If your calling thread has nothing meaningful to do then there is no benefit in creating a separate task that will do the work for you.

However if the calling thread has other things to do, for instance keep the UI responsive, then the calling thread might want to call your function in a separate task, do the other things and when it needs the result await for the result.

Example:

private async YourAsyncWebFunction(...)
{
    // do some processing
    // call other async functions ans wait for it to finish:
    await OtherAsync(...);
    var result = await OtherAsync2(...)
    if (result == ...) { ... }
}

private void MyMainFunction(...)
{
    var taskWebFunction = Task.Run( () => YourAsyncWebFunction(...));
    // do other things
    // after a while: you need the task to finish:
    await taskWebFunction;
    // do other things
 }

If your main function is an event handler you don't have to do Task.Run, just declare the event handler async

The event handler is the only async function that may return void Instead of Task or Task<TResult>

private async void OnButton1_Clicked(object sender, ...)
{
     // do some processing
     await YourAsyncWebFunction(...)
}