在从数据库引擎返回实体时异步处理实体

时间:2015-03-13 17:53:06

标签: c# entity-framework asynchronous async-await entity-framework-6

在EF 6中,我想异步处理从数据库引擎返回的实体。

我知道我可以致电ToListAsync()ForEachAsync(),也许他们会做我正在寻找的事情,但我不相信。我认为我正在寻找的是两者的结合。

根据我的理解,ToListAsync()将在从数据库引擎读取整个查询并转换为实体时完成任务。这意味着您必须等待整个查询在开始处理之前返回。

我无法确定ForEachAsync()是否符合我要求的内容,但由于未在其他地方找到信息,我假设ForEachAsync()仅适用于已检索到的集合,每个项目的处理都是异步的。

当理由从数据库引擎中检索数据时,ForEachAsync()(或另一种未知方法)会调用任务,这是理想的。

所以... ForEachAsync()实际上是这样做的,如果没有,是否有办法完成?

想要这个的原因有两个:

  1. 大型查询不需要作为整体存储在内存中 集合,因此可以节省内存占用和处理能力 更大的结果集不受内存约束
  2. 整体的持续时间 由于处理每个项目,过程可能会缩短 数据检索的延迟
  3. 更新:基本上,如果DbContext在您拨打OnEntityLoaded时为每个实体引发了LoadAsync()之类的事件,我就可以完成我想要的所有事情。因为我可以将实体排入单独的任务处理器,并且可以有效地处理实体并利用任何I / O延迟。我总是可以调整单独的任务处理器,所以我真的不需要EF来支持实体的异步处理,只是在每个实体加载时异步加载和触发事件/调用委托。

    更新2:如果在加载实体时调用了ForEachAsync(),那么这也将完成我所追求的目标。

1 个答案:

答案 0 :(得分:3)

ForEachAsync不同,

ToListAsync并未提前获取所有项目,只是让您对其进行迭代。迭代是async本身。

QueryableExtensions.ForEachAsync代表到IDbAsyncEnumerable.ForEachAsync which is this

internal static async Task ForEachAsync(
    this IDbAsyncEnumerable source, Action<object> action, CancellationToken cancellationToken)
{
    DebugCheck.NotNull(source);
    DebugCheck.NotNull(action);

    cancellationToken.ThrowIfCancellationRequested();

    using (var enumerator = source.GetAsyncEnumerator())
    {
        if (await enumerator.MoveNextAsync(cancellationToken).WithCurrentCulture())
        {
            Task<bool> moveNextTask;
            do
            {
                cancellationToken.ThrowIfCancellationRequested();
                var current = enumerator.Current;
                moveNextTask = enumerator.MoveNextAsync(cancellationToken);
                action(current);
            }
            while (await moveNextTask.WithCurrentCulture());
        }
    }
}

您可以看到它与IEnumerable上的迭代非常类似,但考虑到async-await。我们不是IEnumerableGetEnumeratorIEnumeratorMoveNext,而是IDbAsyncEnumerableGetAsyncEnumeratorIDbAsyncEnumeratorMoveNextAsync

MoveNextAsync允许在需要时实际异步检索项目。