【问题标题】:How to query data asynchronously with Ef Core?如何使用 Ef Core 异步查询数据?
【发布时间】:2021-02-15 19:17:24
【问题描述】:

假设您有一个变量databaseContext,并且想要从用户表中选择所有用户。来自文档

https://docs.microsoft.com/en-us/ef/core/querying/

你可以创建这个方法:

public IEnumerable<User> GetUsers() => _databaseContext.Users;

但这不是同步代码吗?我认为这是完全错误的:

public Task<User[]> GetUsers() => _databaseContext.Users.ToArrayAsync();

我希望有这样的方法:

public Task<IEnumerable<User>> GetUsers() => _databaseContext.Users /* but select them asynchronously */;

由于 EF Core 提供同步和异步方法,例如FindFindAsync 我不知道如何异步查询所有数据。也许 EF Core 会以某种方式在后台执行此操作,但我应该等待它,对吧?

【问题讨论】:

  • 您的 Task 示例将在您等待时返回整个列表。您是否正在寻找像 .AsAsyncEnumerable() ... await foreach(...) 这样的东西来处理返回的记录?
  • 目前我还没有用例。此方法应该只是从数据库中异步查询所有用户。我认为简单地使用_databaseContext.Users 会同步运行,所以它会冻结应用程序,不是吗?
  • 您的情况下的用户是一个 IQueryable,直到您在其上调用 ToList 或 foreach 之类的方法后才实现(同步或异步),此时其背后的表达式将转换为 sql 并执行.现在,这将取决于您实现它时使用的方法。 ToList vs ToListAsync 或 AsAsyncEnumerable 等。单独引用用户不会做任何事情(同步或异步)
  • 啊好吧,所以AsAsyncEnumerable 不返回Task&lt;IEnumerable&lt;T&gt;&gt; 所以我认为没有其他方法可以将返回类型更改为Task&lt;List&lt;T&gt;&gt; 并调用它_databaseContext.Users.ToListAsync()

标签: c# entity-framework-core


【解决方案1】:
  1. 以下代码将异步为您提供所有用户的列表 -
public async Task<IEnumerable<User>> GetUsers() => await _databaseContext.Users.ToListAsync();

这里async 关键字将该方法标识为异步方法(以便您可以相应地调用它),await 关键字确保 - i) 调用线程没有被阻塞,ii) 结果是等待着。哪个线程(调用自己或单独的线程)稍后接收等待的结果取决于任务库如何在后台处理async/await 操作。这正是任务库打算做的事情——从开发人员手中夺走线程的概念,迫使他们根据Task 进行思考,而是提供了更高级别的抽象。

更多-
Dissecting the async methods in C#
https://stackoverflow.com/a/42291461/446519

  1. 这个-
public IEnumerable<User> GetUsers() => _databaseContext.Users;

一种同步方法,但更重要的是它不查询数据库。查询数据库然后返回所有用户列表(同步)调用ToList() -

public IEnumerable<User> GetUsers() => _databaseContext.Users.Tolist();
  1. 这个-
public Task<User[]> GetUsers() => _databaseContext.Users.ToArrayAsync();

也是一个同步方法,它会立即返回一个Task&lt;User[]&gt;实例。但是您可以稍后使用返回的任务对象,异步获取结果,例如 -

Task<User[]> task = this.GetUsers();
User[] users = task.GetAwaiter().GetResult();

编辑:
在上面的第 3 点中,返回 Task&lt;User[]&gt; 后使用的代码纯粹是为了证明这一事实 - “它的 your Task 对象现在,您可以稍后使用它从中获取结果异步”。展示你应该或不应该使用什么方法来做这件事并不是本意。

正如@pinkfloydx33 在评论中建议的那样,.GetAwaiter().GetResult() 可能并不总是获得结果的最佳方法。您可以在不明确将awaiter 设置为 -

的情况下执行相同操作
Task<User[]> myTask = this.GetUsers();
User[] users = await myTask;

【讨论】:

  • 谢谢。只是想知道为什么public async Task&lt;IEnumerable&lt;User&gt;&gt; GetUsers() =&gt; await _databaseContext.Users.ToListAsync();public Task&lt;IEnumerable&lt;User&gt;&gt; GetUsers() =&gt; _databaseContext.Users.ToListAsync(); 不一样,因为第二种方法也应该返回一个挂起的任务?来自 JavaScript 这很好,因为它是一个 Promise,但在 C# 中我似乎仍然需要等待它。
  • 因为Task&lt;List&lt;T&gt;&gt; 不是 Task&lt;IEnumerable&lt;T&gt;&gt; 因为类是不变的 但是,async+await 提供了一些特殊的魔法您可以“思考”返回 List&lt;T&gt; 的方法,它 IEnumerable&lt;T&gt;
  • 我不推荐使用 GetAwaiter+GetResult 除非开发人员完全知道他们在做什么(即他们的名字很可能是 Stephen)。你可以await存储的任务来代替
  • “但是你可以稍后执行返回的任务”不正确。你所拥有的是一项热门任务——它已经开始了。但是,您可以 [a] 等待它完成
  • @Question3r 即使您的第二个代码无法编译,我认为您不是这个意思。要了解您想要了解的内容,您需要对 Task 库如何在后台处理 async/await 操作有所了解。简单地说,当你 await 时,你不会阻塞当前线程(因此你的 UI 没有被冻结)并且你会得到一个 List&lt;User&gt; 异步,但如果你不 await直接返回Task&lt;List&lt;User&gt;&gt;。这取决于你想用它做什么。你可能想检查这个答案 - stackoverflow.com/a/42291461/446519
猜你喜欢
  • 1970-01-01
  • 2023-03-19
  • 1970-01-01
  • 2020-07-09
  • 2020-02-18
  • 2019-03-16
  • 1970-01-01
  • 2020-06-22
  • 1970-01-01
相关资源
最近更新 更多