15

Function need to return Task<List<Record>> Following both options are returning Task<List<Record>>, which one is more efficient? Is there any standard way here?

Option 1 :

Task<List<Record>> GetRecords()
{
    return 
    DbContext.Set<Record>.Where(predicate).ToListAsync();
}

Option 2:

Task<List<Record>> GetRecords()
{
    return
    DbContext.Set<Record>.Where(predicate).AsAsyncEnumerable().ToList();
}

Daniel A. White
  • 187,200
  • 47
  • 362
  • 445
Nainesh Patel
  • 488
  • 2
  • 5
  • 19

3 Answers3

14

No one has actually explained the difference between ToListAsync() and AsAsyncEnumerable().

ToListAsync() will fetch all of the results of the query into memory, and then return the collection of results as a single List<T> object. It will do this asynchronously, so that it doesn't block on I/O, but it won't return anything until all of the results are available.

AsAsyncEnumerable() will "yield" each result as it's available. In the OP's example, they're simply calling ToList(), so the result is the same. However, since C# 8.0 you could also use an "async stream" such as await foreach, and then act on each entity as it's returned. For example:

await foreach (var entity in DbContext.Set<Record>.Where(predicate).AsAsyncEnumerable())
{
    // do something with entity...
}

As to which is "better", it depends on your use-case. If you're just returning a few records to the caller, ToListAsync() should be fine.

However, suppose your query will return 10,000 records. You probably don't want to store all those in memory before you act on them, because the query might run for many seconds or even minutes and will use lots of memory.

In that case, it's probably better to use an async stream. This could be your own async IAsyncEnumerable<T> method which yields its own results. You can then chain these together if needed.

In fact, since ASP.NET Core 6, you can return an IAsyncEnumerable<T> directly from your controller method, and it will "stream" those results back to the caller.

This makes it much more efficient to write code which operates on and/or returns a large number of records, because there's never a point where they're buffered into memory.

Tobias J
  • 19,813
  • 8
  • 81
  • 66
9

While the existing answer by pfx is still true for .NET Core 2.x and earlier, AsAsyncEnumerable has been added to .NET Core 3.x officially. See Ian Kemp's comment for further info.

André Reichelt
  • 1,484
  • 18
  • 52
  • Recapping someone else's comment, is expected to entails some additional information. – Rzassar Sep 16 '20 at 10:42
  • @Rzassar At the time pfx wrote his post, `AsAsyncEnumerable` was still in draft state. That's why I wanted to point out, that it is now an official feature and therefore an alternative to his approach. – André Reichelt Sep 16 '20 at 11:52
  • Okay then, please edit your answer so i up-vote yours :-). – Rzassar Sep 16 '20 at 12:11
  • @Rzassar Well, in this regard, both pfx's and my answer don't really answer the question. The actual question was, which one is more efficient. pfx stated, that you should not use `AsAsyncEnumerable` at all, because it is an internal method of the framework. I have revised this note and found that the statement no longer reflects the state of the art. But in the end, the original question about the more performant of the two variants still has not been answered by either of us. – André Reichelt Sep 16 '20 at 13:09
  • @Rzassar / AndréReichelt I have added an answer which tries to better explain the difference between the two methods. https://stackoverflow.com/a/75525083/393931 – Tobias J Feb 21 '23 at 19:38
7

Note that this is a pre .NET Core 3.x answer.
Find an update in the comment of @IanKemp here below.

Go for option 1 ToListAsync as the source code of AsAsyncEnumerable explicitly mentions

This is an internal API that supports the Entity Framework Core infrastructure and not subject to the same compatibility standards as public APIs. It may be changed or removed without notice in any release. You should only use it directly in your code with extreme caution and knowing that doing so can result in application failures when updating to a new Entity Framework Core release.

The official documentation mentions

This API supports the Entity Framework Core infrastructure and is not intended to be used directly from your code. This API may change or be removed in future releases.

pfx
  • 20,323
  • 43
  • 37
  • 57
  • Thanks @pfx Do you recommend using```Task> GetRecords() { var records = DbContext.Set.Where(predicate).ToList(); return Task.FromResult(records); } ``` – Nainesh Patel May 16 '19 at 22:45
  • 2
    @NaineshPatel Also avoid `Task.FromResult` here and use the `async` features of `Entity Framework` via `ToListAsync`. – pfx May 17 '19 at 10:25
  • 15
    From version 3.0.0 of EF `AsAsyncEnumerable` will be made a fully public first-class citizen: https://github.com/aspnet/EntityFrameworkCore/commit/2795a00b613a5f4f23fca4ad1c53012d17adf7d3 – Ian Kemp Aug 20 '19 at 06:52