如何正确地异步返回对象集合
本文关键字:对象 集合 返回 异步 正确地 | 更新日期: 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没有返回此类型的扩展方法。它有一个ToArrayAsync
和ToListAsync
扩展方法,但这当然需要你在方法中调用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立即查询这些对象,然后传递该集合。
当然你可以把异步列表变成一个异步可枚举对象,但是为什么要这么麻烦呢?一旦数据存在内存中,就没有理由人为地延迟对它的访问。