ToListAsync() 与 AsEnumerable() 在同步性方面

问题描述 投票:0回答:2

假设我有以下方法:

var result = (await db.Students
                      .Where(s => s.Name == "Foo")
                      .ToListAsync())
                      .Select(s => MySuperSmartMethod(s));

MySuperSmartMethod
这里是一个无法翻译成SQL的方法。

正如我从这个answer中了解到的,调用

ToList()
(以及
ToListAsync()
)方法来评估客户端的未来方法有一个问题:它立即查询数据库,迭代结果并将其放入内存中.

因此,最好调用

AsEnumerable()
而不是
ToList()
,因为它不会创建不必要的中间列表。
AsEnumerable()
返回一个
IEnumerable
,因此当执行未来的方法时,它们将直接在 IEnumerable 中迭代对象。

因此我得出结论,我应该将之前的方法重写为如下所示:

var result = db.Students
               .Where(s => s.Name == "Foo")
               .AsEnumerable()
               .Select(s => MySuperSmartMethod(s));

好的,但现在我有另一个问题:我的方法不再是异步的。

那么,我该怎么办?还有其他方法吗?异步查询数据库是否会给 ASP.NET Core 应用程序带来任何性能优势?或者我应该重写我的异步 MediatR 查询和命令以同步替换

ToListAsync()
AsEnumerable()
只要有可能?

c# linq asp.net-core entity-framework-core linq-to-sql
2个回答
1
投票

您能做的就是将其保留为

IAsyncEnumerable
。这意味着在您使用
ToListAsync

枚举查询之前,查询实际上并未执行
var result = db.Students
               .Where(s => s.Name == "Foo")
               .AsAsyncEnumerable()
               .Select(s => MySuperSmartMethod(s));
// much later
var list = await result.ToListAsync(someCancellationToken);

为此,您需要安装

System.Linq.Async
软件包

理论上,您可以推出自己的

Select
扩展,但请注意,只需执行
await (foreach
就会立即开始枚举。


0
投票

在 EF 中使用异步方法是静态的“嘿,这可能需要一段时间,所以如果其他人在等待,你可以继续”。它对于队列中等待它的代码的执行时间没有任何作用(重要或积极)。因此,根据此代码实际上是否需要一段时间,使其保持同步可能根本没有问题。

async
并不是提高性能的灵丹妙药,如果有什么不同的话,它会让每个查询比保持同步时稍微“慢一点”。对于可能需要一秒或更长时间才能执行的内容(通常我设置大约 300-500 毫秒的阈值)或由我希望非常频繁执行的方法调用的内容来说,这非常有用。 恕我直言,最好的解决方案是避免您认为在查询表达式中需要“SuperSmartMethod”的情况。当“SuperSmartMethod”出现在

Select

;

 中时,投影可以帮助解决此问题,其中“SuperSmartMethod”可以在视图模型而不是查询中进行计算
例如,如果 SuperSmartMethod 需要来自一个或多个实体的值 A、B 和 C,请将 A、B 和 C 加载到 ViewModel 中,并在 ViewModel 中公开“SuperSmart”属性或函数。查询保持纯粹,并且

async

兼容。

如果您需要在查询表达式中保留“SuperSmartMethod”,另一种解决方案是使用 

async IAsyncEnumerable

/w

yield
将查询方法包装在方法中。
编辑
删除这个例子,直到我可以玩它。它看起来不像是开箱即用的,因为它期望在查询方法中等待一些东西......

© www.soinside.com 2019 - 2024. All rights reserved.