假设我有以下方法:
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()
只要有可能?
您能做的就是将其保留为
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
就会立即开始枚举。
在 EF 中使用异步方法是静态的“嘿,这可能需要一段时间,所以如果其他人在等待,你可以继续”。它对于队列中等待它的代码的执行时间没有任何作用(重要或积极)。因此,根据此代码实际上是否需要一段时间,使其保持同步可能根本没有问题。
async
并不是提高性能的灵丹妙药,如果有什么不同的话,它会让每个查询比保持同步时稍微“慢一点”。对于可能需要一秒或更长时间才能执行的内容(通常我设置大约 300-500 毫秒的阈值)或由我希望非常频繁执行的方法调用的内容来说,这非常有用。
恕我直言,最好的解决方案是避免您认为在查询表达式中需要“SuperSmartMethod”的情况。当“SuperSmartMethod”出现在 Select
;
中时,投影可以帮助解决此问题,其中“SuperSmartMethod”可以在视图模型而不是查询中进行计算例如,如果 SuperSmartMethod 需要来自一个或多个实体的值 A、B 和 C,请将 A、B 和 C 加载到 ViewModel 中,并在 ViewModel 中公开“SuperSmart”属性或函数。查询保持纯粹,并且
async
兼容。
如果您需要在查询表达式中保留“SuperSmartMethod”,另一种解决方案是使用async IAsyncEnumerable
/w
yield
将查询方法包装在方法中。 编辑删除这个例子,直到我可以玩它。它看起来不像是开箱即用的,因为它期望在查询方法中等待一些东西......