【发布时间】:2021-10-15 11:04:49
【问题描述】:
我使用的是 EF Core 5.0 并且有以下代码:
public async IAsyncEnumerable<Item> GetItems([EnumeratorCancellation] CancellationToken cancellationToken = default)
{
await using var ctx = _DbContextFunc();
//Isolationlevel is required to not cause any issues with parallel working on already read items
await ctx.Database.BeginTransactionAsync(IsolationLevel.ReadUncommitted, cancellationToken).ConfigureAwait(false);
await foreach (var item in ctx.Item.
.AsSplitQuery()
.Include(i => i.ItemDetail1)
.Include(i => i.ItemDetail2)
.OrderByDescending(i => i.ItemId)
.AsNoTracking()
.AsAsyncEnumerable()
.WithCancellation(cancellationToken))
{
yield return item;
}
}
它按预期工作,允许我在加载更多数据的同时填充数据网格。如果我取消提供的 CancelationToken,首先我会在预期的 MoveNextAsync() 行上得到一个 TaskCanceledException。
但是:我可以在 SQL Profiler 中看到 SQL 查询本身并没有中止,而是一直运行,直到所有数据都加载完毕,然后我才在同一行收到第二个 TaskCanceledException。
如何中止查询本身?
更新
我将 AsSplitQuery() 添加到示例中,因为事实证明这是我所经历的行为的原因(正如 Ivan 正确猜测的那样)。为了使样本更短而忽略了它...
【问题讨论】:
-
要知道在
OrderByDescending查询中提供哪个项目,它是否必须查询整个表才能只提供一个结果? -
为什么你认为这个查询只返回一个项目?
-
我的意思是,使用
yield关键字一次返回一个。我想这可以让你停止枚举器枚举,但枚举器不需要执行整个 SQL 事务来产生一个项目吗? -
如果不出意外,也许您可以将您的交易包装在
using(var tran = await ctx.Database.BeginTransactionAsync(IsolationLevel.ReadUncommitted, cancellationToken).ConfigureAwait(false)) { ... }中,然后在处理取消令牌时,您可以尝试启动tran.Rollback()。我不知道这是否可行,但这是我目前唯一能想到的。 -
不,它作为流很好地工作,我可以从探查器中看到,即使提供了第一个结果,查询也没有完成(原始查询比这里显示的要复杂一些)但这不应该是问题)。我会尝试回滚并告诉你它有帮助