2015-04-22 100 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