异步处理从数据库引擎返回的实体
Asynchronously process entities as they are returned from the database engine
在 EF 6 中,我想异步处理从数据库引擎 return 编辑的实体。
我知道我可以打电话给 ToListAsync()
和 ForEachAsync()
,也许他们会做我正在寻找的事情,但我不相信。我想我要找的是两者的结合。
据我了解,当从数据库引擎读取整个查询并将其转换为实体时,ToListAsync()
将完成任务。这意味着您必须等待整个查询 return 才能开始处理。
我无法确定 ForEachAsync()
是否满足我的要求,但由于在其他地方找不到信息,我假设 ForEachAsync()
只是对已经检索到的内容起作用收集和处理每个项目是异步的。
理想的情况是 ForEachAsync()
(或另一种未知方法)在从数据库引擎检索数据时调用任务。
那么... ForEachAsync()
是否真的这样做了,如果没有,是否有办法做到这一点?
想要这个的原因有两个:
- 大型查询不需要作为整个存储在内存中
收集,因此节省了内存占用和处理能力
不受内存限制的更大的结果集
- 整个时长
由于在处理每个项目期间,过程可能会更短
数据检索延迟
更新: 基本上,如果 DbContext
在您调用 LoadAsync()
时为每个实体引发了类似 OnEntityLoaded
的事件,我可以完成我想要的一切。因为我可以将实体排入一个单独的任务处理器,并且可以有效地处理实体并利用任何 I/O 延迟。我总是可以调整单独的任务处理器,所以我真的不需要 EF 来支持实体的异步处理,只需异步加载并在每个实体加载时触发 event/call 委托。
更新 2: 如果在加载实体时调用 ForEachAsync()
,那么这也将完成我所追求的。
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
。我们有 IDbAsyncEnumerable
, GetAsyncEnumerator
, IDbAsyncEnumerator
and MoveNextAsync
.
而不是 IEnumerable
、GetEnumerator
、IEnumerator
和 MoveNext
MoveNextAsync
允许在需要时实际异步检索项目。
在 EF 6 中,我想异步处理从数据库引擎 return 编辑的实体。
我知道我可以打电话给 ToListAsync()
和 ForEachAsync()
,也许他们会做我正在寻找的事情,但我不相信。我想我要找的是两者的结合。
据我了解,当从数据库引擎读取整个查询并将其转换为实体时,ToListAsync()
将完成任务。这意味着您必须等待整个查询 return 才能开始处理。
我无法确定 ForEachAsync()
是否满足我的要求,但由于在其他地方找不到信息,我假设 ForEachAsync()
只是对已经检索到的内容起作用收集和处理每个项目是异步的。
理想的情况是 ForEachAsync()
(或另一种未知方法)在从数据库引擎检索数据时调用任务。
那么... ForEachAsync()
是否真的这样做了,如果没有,是否有办法做到这一点?
想要这个的原因有两个:
- 大型查询不需要作为整个存储在内存中 收集,因此节省了内存占用和处理能力 不受内存限制的更大的结果集
- 整个时长 由于在处理每个项目期间,过程可能会更短 数据检索延迟
更新: 基本上,如果 DbContext
在您调用 LoadAsync()
时为每个实体引发了类似 OnEntityLoaded
的事件,我可以完成我想要的一切。因为我可以将实体排入一个单独的任务处理器,并且可以有效地处理实体并利用任何 I/O 延迟。我总是可以调整单独的任务处理器,所以我真的不需要 EF 来支持实体的异步处理,只需异步加载并在每个实体加载时触发 event/call 委托。
更新 2: 如果在加载实体时调用 ForEachAsync()
,那么这也将完成我所追求的。
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
。我们有 IDbAsyncEnumerable
, GetAsyncEnumerator
, IDbAsyncEnumerator
and MoveNextAsync
.
IEnumerable
、GetEnumerator
、IEnumerator
和 MoveNext
MoveNextAsync
允许在需要时实际异步检索项目。