“ToListAsync()”和“AsAsyncEnumerable().ToList()”之间的区别

Nai*_*tel 4 c# tolist async-await entity-framework-core iasyncenumerable

函数需要返回Task<List<Record>> 以下两个选项都返回Task<List<Record>>,哪个更有效?这里有标准的方法吗?

选项1 :

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

Run Code Online (Sandbox Code Playgroud)

选项 2:

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

Run Code Online (Sandbox Code Playgroud)

Tob*_*s J 19

ToListAsync()没有人真正解释过和之间的区别AsAsyncEnumerable()

ToListAsync()会将查询的所有结果提取到内存中,然后将结果集合作为单个List<T>对象返回。它将异步执行此操作,以便不会阻塞 I/O,但在所有结果可用之前不会返回任何内容。

AsAsyncEnumerable()将“产生”每个可用的结果。在OP的示例中,他们只是调用ToList(),所以结果是相同的。但是,从 C# 8.0 开始,您还可以使用“异步流”,例如await foreach,然后在返回时对每个实体进行操作。例如:

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

至于哪个“更好”,这取决于您的用例。如果您只是向调用者返回一些记录,ToListAsync()应该没问题。

但是,假设您的查询将返回 10,000 条记录。在对它们进行操作之前,您可能不想将所有这些存储在内存中,因为查询可能会运行很多秒甚至几分钟,并且会使用大量内存。

在这种情况下,最好使用异步流。这可能是你自己的async IAsyncEnumerable<T>方法,yield有它自己的结果。如果需要,您可以将它们链接在一起。

事实上,从 ASP.NET Core 6 开始,您可以直接从控制器方法返回一个IAsyncEnumerable<T>,它会将这些结果“流”回调用者。

这使得编写操作和/或返回大量记录的代码更加高效,因为它们永远不会被缓冲到内存中。


pfx*_*pfx 7

请注意,这是 .NET Core 3.x 之前的答案。
在下面 @IanKemp 的评论中查找更新。

选择选项 1ToListAsync作为明确提到的源代码AsAsyncEnumerable

这是一个内部 API,支持 Entity Framework Core 基础设施,并且不受与公共 API 相同的兼容性标准的约束。它可能会在任何版本中更改或删除,恕不另行通知。您应该极其谨慎地在代码中直接使用它,并且知道这样做可能会导致更新到新的 Entity Framework Core 版本时出现应用程序故障。

官方文档 提到

此 API 支持 Entity Framework Core 基础结构,并不适合直接在您的代码中使用。此 API 可能会在未来版本中更改或删除。

  • 从 EF `AsAsyncEnumerable` 版本 3.0.0 开始,将成为完全公开的一等公民:https://github.com/aspnet/EntityFrameworkCore/commit/2795a00b613a5f4f23fca4ad1c53012d17adf7d3 (16认同)
  • @NaineshPatel 在这里还要避免使用“Task.FromResult”,并通过“ToListAsync”使用“Entity Framework”的“async”功能。 (2认同)

And*_*elt 7

虽然 pfx 的现有答案对于 .NET Core 2.x 及更早版本仍然适用,但AsAsyncEnumerable已正式添加到 .NET Core 3.x 中。有关更多信息,请参阅 Ian Kemp 的评论。