为什么我在 EF Core 中调用 IAsyncEnumerable<T> 不异步枚举?

Why does my IAsyncEnumerable<T> call in EF Core not enumerate asynchronously?

我正在编写一个 C# 方法,该方法从 SQL 查询(不是直接的 DBSet<T>!)流式传输大量行,对它们执行一些转换,然后将结果写入MongoDB 数据库。我正在尝试尽快完成此 运行,并且由于相当高的网络延迟,我想避免多次 return 访问 SQL 服务器。

我有一个 class、StreamlinedCrmTicket,它表示一个 DTO,EF 将原始 SQL 查询的结果投射到该 DTO 上,该查询不采用参数化输入。我正在使用 EF Core 3.1.6 和 .Set<StreamlinedCrmTicket> 技术来执行原始 SQL 查询。然后我使用 .AsNoTracking() 来提高性能,因为这只是一个读取操作。最后,我调用 .AsAsyncEnumerable(),并将整个 shebang 包装在 await foreach 中,而后者又位于标记为 async.

的方法中

整个事情看起来像这样:

await foreach (var ticket in _affinityContext.Set<StreamlinedCrmTicket>().FromSqlRaw(query).AsNoTracking().AsAsyncEnumerable().WithCancellation(cancellationToken))
{
   // Do something with each ticket.
}

我的原始 SQL 查询的来源 table 当前包含大约 120 万行。使用 SSMS 测量时,有少数联接似乎对查询的执行时间造成的变化很小。

当我执行我的代码时,似乎 EF 启动了查询,但是 foreach 循环的主体,无论它包含什么,直到整个查询执行完毕并且从 SQL服务器。这违背了我使用 IAsyncEnumerable 的目的!我的理解是 IAsyncEnumerable 应该允许我对行(或实体)进行操作,因为它们来自数据库 return,而不必等待整个结果集。

一些支持我的理论的观点是:

我不确定为什么这是 运行 同步,并且 Microsoft 站点上的文档似乎很差!

您的原始 SQL 不提供用于分页的游标,因此 SQL 服务器必须 return 一次性获得整个结果。

作为参考,GSerg 在评论中建议我的查询可能无法流式传输。在这种情况下,在查询中使用 LEFT OUTER JOIN 导致在 SQL 服务器中使用哈希匹配。这会阻止查询结果集流式传输。