.NetCore WebAPI methods Async or Not with EFCore query

asp.net-core async-await asynchronous c# entity-framework-core

Question

What is considered best(or better) practice to have as default state and why, and what are the real benefits of either?
For example if we have Get method in controller should it be:

public virtual IActionResult Get(int page = 1, int pageSize = 100)
{
    var q = repository.Query().AsNoTracking().ProjectTo<UserViewModel>();
    q = q.Skip((page - 1) * pageSize).Take(pageSize);
    return new OkObjectResult(q);
}

or

public virtual async Task<IActionResult> Get(int page = 1, int pageSize = 100)
{
    var q = repository.Query().AsNoTracking().ProjectTo<UserViewModel>();
    q = q.Skip((page - 1) * pageSize).Take(pageSize);
    return new OkObjectResult(await q.ToListAsync());
}

If async is prefered is this code good for it and enough?
Also Query() in repository is simply
public virtual IQueryable<User> Query() { return context.Set<User>().AsQueryable(); }

I have found here that async was in fact slower: https://github.com/aspnet/EntityFramework/issues/5816
That bug was apparently fixed, but I don't know what are performance comparison in the latest version.

1
1
7/4/2017 1:04:48 PM

Accepted Answer

The benefits of async/await over regular, synchronized code, is biggest when your server is under heavy enough load that freeing up threads on the thread pool makes a difference. Before then, the overhead of context tracking and switching can easily be bigger than the performance gains of freeing up threads.

I tend to favor using the asynchronous API over the synchronous, mainly because I've been in a few projects where high load and synchronous code was a high enough problem that someone decided it was time to refactor and move to async, and it was a pain every time. It's also becoming more and more common to only expose async API:s (for example, the Azure SDK has many async actions which don't have synchronous counterparts), which means that you might have to perform such a re-write later, if you end up using such an API.

But if performance is the only thing you want to take into account, you need to profile your application under realistic load and see which approach works best.


Addendum:

It's quite easy to introduce more of the async/await overhead than necessary. A good rule-of-thumb I like to use is to think of three types of methods that can return Task or Task<T>, and follow one of these patterns for each, respectively:

  • Methods that are actually synchronous (but return Task or Task<T>, e.g. in order to allow for overloading with asynchronous variants): return with Task.FromResult.

    public Task<string> ActuallySynchronousGetter()
    {
        return Task.FromResult("foo");
    }
    
  • Methods that forward to an asynchronous method, but don't do anything else: just return the Task, no need to await it.

    // note: no async/await, just Task<T>
    public Task<string> ForwardingGetter()
    {
         return SomeOtherGetter(); // also with return type Task<string>
    }
    
  • Actual asynchronous methods, that both do something asynchronous (usually I/O) and then work with the result of that asynchronous operation: this is the only time you actually need the async and await keywords.

    public async Task<string> GetStringAsynchronously()
    {
         var str = await SomeOtherGetter();
         return str.Replace("foo", "bar"); // note: we need the actual string here
    }
    

Your code seems to fall in the third case - you need the result of .ToListAsync() in order to pass it to the OkObjectResult constructor. I would probably have written the entire thing as one statement, but it's mostly a matter of taste:

var data = await repository.Query()
    .AsNoTracking()
    .ProjectTo<UserViewModel>()
    .Skip((page - 1) * pageSize)
    .Take(pageSize)
    .ToListAsync();
return new OkObjectResult(data);

Stephen Cleary writes a lot and in depth about how async and await works, and how you should (and should not) use it.

4
7/4/2017 11:56:34 AM


Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow