Doesn't wait when you use ForEachAsync with wait inside Action

The following should return "C", but returns "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; 

It does not wait for DoStuffAsync to complete, state="C" is executed, and then state="B" executes (because inside it is still waiting).

+1
c # asynchronous entity-framework async-await
Apr 22 '15 at 3:19
source share
1 answer

This is because the ForEachAsync implementation does not expect delegated action.

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

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

But this is because you cannot wait for the action, the delegate must be Func, which returns the task - see How do you implement the delegation method of asynchronous actions?

Therefore, until Microsoft provides a signature that includes the Func delegate and calls it waiting, you will have to reverse your own extension method. At the moment I am using the following.

 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)); } } } 

In this case, the source test code in your OP will work as expected.

+3
Apr 22 '15 at 3:19
source share



All Articles