Entity Framework Async Performance

Asked

Viewed 257 times

5

I am implementing an asynchronous webapi, and have tested two ways to return the value with EF6. The 1st form was using Tolistasync(), and the 2nd form was using Task. The 2nd form has a much better performance than the second. I was wondering if I should use the 2nd form or 1st.

Code using Tolistasync()

public Task<List<Holiday>> All()
{
    return _dbSet.AsNoTracking().ToListAsync();
}

Code using Task

public Task<List<Holiday>> All()
{
    return Task.FromResult(_dbSet.AsNoTracking().ToList());
}

Controller code calling these methods:

public async Task<ObjectResult> Get()
{
    try
    {
        var result = await _appService.All();

        return this.Ok(result);
    }
    catch (Exception ex)
    {
        return this.BadRequest("ERROR: " + ex.Message);
    }
}
  • If I’m not mistaken Task.FromResult() synchronous wheel, so only the first actually is asynchronous

  • "The second form has a much better performance than the second". Which one has a much better performance?

  • Hi @jbueno public Task<List<Holiday>> All() { Return Task.Fromresult(_dbSet.Asnotracking().Tolist(); }

1 answer

5

The former is being misused. The method needs to be public async Task<List<Holiday>> and you must use the await (equal to the action of your Controller):

public async Task<List<Holiday>> All()
{
    return await _dbSet.AsNoTracking().ToListAsync().ConfigureAwait(false);
}

Use asynchronous methods whenever available (e.g. EF provides, so try to use it whenever you can) server, this helps/facilitates scalability with respect to the ability of the IIS to fulfill requests. In GUI applications, the await/async causes the screen to not lock while some code is executed and/or is awaiting the response of some resource (query to a database, called the API, webservices).

TL;DR

The example of coffee and water

Imagine that on a table there’s a water cooler, a coffee bottle and glass glasses, but there’s few glasses. The people who arrived in front take the cup for use (whether for water or coffee). Some people have run out of glass, so they should wait for the others to finish using, and then - after washing the glass - use it.

The problem is that the coffee bottle is empty. Hence, those who wanted coffee are there with the cup in their hand waiting until they finish passing the coffee and filling the bottle, preventing the other people who ran out of glass and wanted water to use the cup (because there were few cups, there was not enough for everyone). Dirty, right? It would give other people time to drink water, wash the glass and return to the person of the coffee who was still waiting.

Coffee and water are resources, the glasses are the threads of pool of threads IIS to fulfill requests, people are customers (whether users or other applications making requests) who will consume these resources.

Using asynchronous code, it would be like the coffee person does not keep the cup with her (the method does not block the thread of IIS) as waiting for coffee (while the method awaits the return of a query in the bank, or a call to an API/WCF), freeing the cup for another person to use (the method with await releases the thread for other requests to be met).

If the coffee arrives fast, it may occur to that person having to wait around while the other finishes drinking water, so that he can drink his coffee (i.e., if the consultation ends, it may be that action need to expect to have a thread available to continue its execution and return to the user), or it may be that the person has already finished drinking his water (the second requisition has already finished and released the thread back for the first requisition that had originally used that thread).

Certainly, asynchronously, the total waiting time for both (summing everything) is less than synchronously.

On the other hand, if everyone wanted the coffee, there would be no way: everyone would have to wait (that is, if all the requests were a query to the database and the same was stuck, it would not help much, because everyone would be waiting anyway).


I don’t know what you meant by the second way to have performance far better than the "second", but if you do tests, use methods Async (like the ToListAsync instead of just ToList) use yes a slightly larger "overload" (processing), making it slower, however I assure that the difference is so small that it should not cause the least impact on its application.

The use of asynchronous routines in GUI applications (such as WPF and Winforms) serves to not halt the "screen", leaving the application responsive while awaiting the return of some external method/resource (such as a call to an API, Webservice, query to a database and other I/O operations). Already in applications server (WCF and Webapplication, for example), is about releasing the service of new requests by the IIS, helping in the scalability of the same.

I understand you should use the methods Async (as ToListAsync()) whenever available from libraries (such as EF6) you may use.

It doesn’t mean that you’ll be working with parallelism or that it will make your methods faster (in fact, as in some cases they might start a new thread for the execution of one of these routines, it has the processing cost to create this new thread, and then the cost to return to thread main), but this facilitates when there is need to scale your application.

For a better understanding, I suggest reading this publication of Stephen Cleary, he’s a myth on this subject.


Example with synchronous requests

Just a simple example: suppose the pool of threads IIS had only 2 threads for the fulfillment of requests, and you are using only synchronous methods.

Let’s say a first user accessed a page, which will invoke an action, which will call an external API (paypal, facebook, anything else) and this operation takes a few seconds. One thread is consumed for the requisition (remaining only 1) and its action will synchronously await the return of this API.

Meanwhile, another user accessed another page, which will consult something in the database to do some kind of processing (that takes a few more seconds). Other thread is used and will only be released when the action controller return a response.

If a third user tries to access any other page, his request will be waiting for one of the two threads are released, that is, only when one of the actions end its execution (which depends on external resources such as API and database, which in turn may take).

The same example, but asynchronously

When the first action call an external API using await, to thread of IIS becomes available again, so that when that third user arrives, his request will be immediately met.

Suppose this third requisition consumed the thread that was free (that had been original used by the first request) and the second is doing some processing, occupying the other thread. In other words, both threads are currently busy. If the API on the first request has returned with a response, the default behavior will be action try to recover the thread of the IIS again to continue its execution, but it will not succeed because the two are busy.

This third request calls a WCF on a server backend to do some processing, using await, releasing the thread, which will be consumed by the first requisition, only to finish the action and return a reply to the user, releasing again the thread, and the third requisition got a return from the WCF and consumed again the thread, also to just finish the action and return reply to user.

In that case, the time thread is busy with some of the requisitions, just the time to enter the action of controller and call the await.

Of course the IIS can handle a lot more requests than just 2, it was just to illustrate, but if your application grows a lot, then it may start to make a difference. But the interesting thing is to use good practice from an early age, instead of waiting for the problem to arise and then modify all your code.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.