IAsyncEnumerable 的早期 Disposable 需要永远

Early Disposable of an IAsyncEnumerable Takes Forever

我有一个查询,我正在 运行 使用 EF Core 3.1 和 Postgresql 对我的数据库进行查询,根据请求的参数,有时 return 可能会产生一个很大的结果集。我正在使用 Async Enumerable 流式传输结果,有时可能需要一分钟多的时间才能完成:

try
{
    IAsyncEnumerable<Entity> ae = ctxt.Entity.Where(e => e.Value > 5).AsAsyncEnumerable();

    await foreach(Entity e in ae.ConfigureAwait(false))
    {
        //Do some work on each one here that could potentially throw an exception
    }
}
catch(Exception ex)
{
    //This code can take over a minute after the exception is thrown to begin executing
}

然后我摆脱了 await foreach 循环以更好地了解发生了什么:

IAsyncEnumerable<Entity> ae = ctxt.Entity.Where(e => e.Value > 5).AsAsyncEnumerable();
var enumerator = ae.GetAsyncEnumerator();
await enumerator.MoveNextAsync();
await enumerator.DisposeAsync();
Console.WriteLline("Done Disposing");

并且发现 DisposeAsync 调用是花费时间的地方。我回到 await foreach 并能够确认在循环内使用 break 表现出相同的行为。

接下来,我尝试创建一个非 ef IAsyncEnumerable 以查看此行为是否与实体框架隔离。

private async IAsyncEnumerable<int> IntEnumerator()
{
    await Task.Delay(0).ConfigureAwait(false);

    for(int i = 0; i < int.MaxValue; i++)
    {
      yield return i;
    }
}

使用此代码,Dispose 是即时的。

IAsyncEnumerable<int> ae = IntEnumerator();
var enumerator = ae.GetAsyncEnumerator();
await enumerator.MoveNextAsync();
await enumerator.DisposeAsync();
Console.WriteLline("Done Disposing");

这让我相信这个问题是 EF 核心特有的,但我不确定这是他们实现中的错误,还是我做错了什么。有没有其他人看到过类似的情况并找到了解决方案?

我想在 EF github 页面上打开问题之前先查看这里。

看起来这实际上是 postgresql 的问题。 See the issue I opened on the Npgsql github page。根据维护者的说法,它是不可解析的。