Async await using LINQ ForEach()

No. It isn’t. This ForEach doesn’t support async-await and requires your lambda to be async void which should only be used for event handlers. Using it will run all your async operations concurrently and won’t wait for them to complete.

You can use a regular foreach as you did but if you want an extension method you need a special async version that iterates over the items, executes an async operation and awaits it.

However, you can create one:

Staring from .NET 6.0 you can use Parallel.ForEachAsync:

public async Task ForEachAsync<T>(this IEnumerable<T> enumerable, Func<T, Task> action)
{
    await Parallel.ForEachAsync(
        enumerable,
        async (item, _) => await action(item));
}

Or, avoid the extension method and just call it directly:

await Parallel.ForEachAsync(
    RequiredSinkTypeList,
    async (sinkName, _) =>
    {
        var sinkType = new SinkType() { Name = sinkName };
        context.SinkTypeCollection.Add(sinkType);
        await context.SaveChangesAsync().ConfigureAwait(false);
    });

On older platforms you need to use foreach:

public async Task ForEachAsync<T>(this IEnumerable<T> enumerable, Func<T, Task> action)
{
    foreach (var item in enumerable)
    {
        await action(item);
    }
}

Usage:

internal static async Task AddReferencseData(ConfigurationDbContext context)
{
    await RequiredSinkTypeList.ForEachAsync(async sinkName =>
    {
        var sinkType = new SinkType() { Name = sinkName };
        context.SinkTypeCollection.Add(sinkType);
        await context.SaveChangesAsync().ConfigureAwait(false);
    });
}

A different (and usually more efficient) implementation of ForEachAsync would be to start all the async operations and only then await all of them together but that’s only possible if your actions can run concurrently which isn’t always the case (e.g. Entity Framework):

public Task ForEachAsync<T>(this IEnumerable<T> enumerable, Func<T, Task> action)
{
    return Task.WhenAll(enumerable.Select(item => action(item)));
}

As was noted in the comments you probably don’t want to use SaveChangesAsync in a foreach to begin with. Preparing your changes and then saving them all at once will probably be more efficient.

Leave a Comment

Hata!: SQLSTATE[HY000] [1045] Access denied for user 'divattrend_liink'@'localhost' (using password: YES)