0

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

我有一个类,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 查询的源表当前包含大约 120 万行。当使用 SSMS 测量时,有一些连接似乎对查询的执行时间几乎没有变化。

当我执行我的代码时,似乎 EF 启动了查询,但 foreach 循环的主体,无论它包含什么,都不会开始执行,直到整个查询已执行并从 SQL Server 接收到结果集。这违背了我使用 IAsyncEnumerable 的目的!我的理解是 IAsyncEnumerable 应该允许我在行(或实体)从数据库返回时对其进行操作,而无需等待整个结果集。

一些支持我的理论的想法,即目前这不是异步行为:

  • 一旦调用_affinityContext.Set<StreamlinedCrmTicket>().FromSqlRaw(query).AsNoTracking().AsAsyncEnumerable().WithCancellation(cancellationToken)完成,就会开始大量的网络 IO。我可以在我的 Windows 机器上的性能监视器中看到,IO 是与我的代码应该运行的服务器的 SQL Server 连接。
  • 我将foreach循环体换成了一个非常简单的循环体,它只在网络 IO 停止后运行。
  • ORDER BY从 SQL 查询中删除了所有子句——在这个用例中行排序无关紧要,我担心这可能会导致查询在返回第一行之前需要很长时间,从而产生同步运行的错觉。然而,网络 IO 表明这不是(而且不是 - 我把条款省略了!)的情况。
  • 如果我在查询中添加一个TOP 1000语句SELECT,它的执行速度会更快。

我不确定为什么这是同步运行的,而且微软网站上的文档似乎很差!

4

2 回答 2

0

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

于 2020-07-31T21:13:11.860 回答
-1

您的原始 SQL 不提供用于分页的游标,因此 SQL Server 必须一次性返回整个结果。

于 2020-07-27T21:33:22.790 回答