2015-04-22 60 views
1

下应该返回“C”不使用ForEachAsync时等待,但它返回“B”与等待内部操作

using System.Data.Entity; 
//... 
var state = "A"; 
var qry = (from f in db.myTable select f); 
await qry.ForEachAsync(async (myRecord) => { 
    await DoStuffAsync(myRecord); 
    state = "B"; 
}); 
state = "C"; 
return state; 

它不会等待DoStuffAsync通过,然后完成,state="C"运行稍后state="B"执行(因为里面它仍在等待)。

回答

3

这是因为ForEachAsync的实现不等待委托动作

moveNextTask = enumerator.MoveNextAsync(cancellationToken); 
action(current); 

看到https://github.com/mono/entityframework/blob/master/src/EntityFramework/Infrastructure/IDbAsyncEnumerableExtensions.cs#L19

但是那是因为,你不能等待一个动作,委托必须是一个Func键它返回一个任务 - 请参阅How do you implement an async action delegate method?

因此,直到Microsoft提供包含Func委托并使用await调用它的签名之后,您将不得不推出自己的扩展方法。目前我正在使用以下内容。

public static async Task ForEachAsync<T>(
    this IQueryable<T> enumerable, Func<T, Task> action, CancellationToken cancellationToken) //Now with Func returning Task 
{ 
    var asyncEnumerable = (IDbAsyncEnumerable<T>)enumerable; 
    using (var enumerator = asyncEnumerable.GetAsyncEnumerator()) 
    { 

     if (await enumerator.MoveNextAsync(cancellationToken).ConfigureAwait(continueOnCapturedContext: false)) 
     { 
      Task<bool> moveNextTask; 
      do 
      { 
       var current = enumerator.Current; 
       moveNextTask = enumerator.MoveNextAsync(cancellationToken); 
       await action(current); //now with await 
      } 
      while (await moveNextTask.ConfigureAwait(continueOnCapturedContext: false)); 
     } 
    } 
} 

有了这个,OP中的原始测试代码将按预期工作。

+0

我不确定自己的ForEachAsync会如何与Action版本一起使用。我只是删除了使用System.Data.Entities;并有我自己的命名空间。 – Todd