如何正确地异步返回对象集合

本文关键字:对象 集合 返回 异步 正确地 | 更新日期: 2023-09-27 18:18:42

我需要在核心接口中定义返回列表的方法。我的项目在很大程度上依赖于async/await的使用,所以我需要尽可能地将核心引用/接口定义为异步的。我也使用EF7作为我的数据访问层。我现在到处都用IAsyncEnumerable

我目前正在决定是否继续使用IAsyncEnumerable或恢复使用Task<IEnumerable<T>>IAsyncEnumerable在这一点上似乎很有希望。EF7也在使用它。问题是,我不知道也想不出怎么用它。网站上几乎没有任何内容告诉任何人如何使用Ix.Net。有一个ToAsyncEnumerable扩展,我可以在IEnumerable对象上使用,但这不会异步做任何事情(或者它?? ?)。另一个缺点是给定下面的签名:

IAsyncEnumerable GetPersons();

因为这不是一个返回Task的函数,所以我不能在函数块中使用async/await。

另一方面,我的直觉告诉我应该坚持使用Task<IEnumerable<T>>。当然这也有它的问题。EF没有返回此类型的扩展方法。它有一个ToArrayAsyncToListAsync扩展方法,但这当然需要你在方法中调用await,因为Task<T>不是协变的。这可能是一个问题,因为这会创建一个额外的操作,如果我简单地返回Task对象,可以避免这个操作。

我的问题是:我应该继续使用IAsyncEnumerable(首选)还是我应该把一切都改回Task<IEnumerable<T>>(不首选)?我也愿意接受其他的建议。

如何正确地异步返回对象集合

我会选IAsyncEnumerable。它允许你保持你的操作既异步又懒惰。

如果没有它,你需要返回Task<IEnumerble>,这意味着你将所有结果加载到内存中。在许多情况下,这意味着查询和占用比需要的更多的内存。

经典的情况是用户调用Any的查询。如果是Task<IEnumerable>,它将首先将所有结果加载到内存中,如果是IAsyncEnumerable,则加载一个结果就足够了。

同样相关的是,在Task<IEnumerable>中,你需要同时在内存中保存整个结果集,而在IAsyncEnumerable中,你可以一次"流式"处理几个结果。

同时,这也是生态系统的发展方向。它是由响应式扩展(reactive extension)和Stephen Toub本周建议的一个新库添加的,可能会在c#的下一个版本中得到原生支持。

您应该使用Task<IEnumerable<T>>返回类型。原因很简单,您不希望为每个想要读取的对象对数据库运行一个新的查询,因此只需让EF立即查询这些对象,然后传递该集合。

当然你可以把异步列表变成一个异步可枚举对象,但是为什么要这么麻烦呢?一旦数据存在内存中,就没有理由人为地延迟对它的访问。