score:18

Accepted answer

async lambda expression cannot be converted to simple Func<TSource, TResult>.

So, select many cannot be used. You can run in synchronized context:

myEnumerable.Select(c => Functions.GetDataAsync(c.Id)).SelectMany(task => task.Result);

or

List<DataItem> result = new List<DataItem>();

foreach (var ele in myEnumerable)
{
    result.AddRange(await Functions.GetDataAsyncDo(ele.Id));
}

You cannot neither use yield return - it is by design. f.e.:

public async Task<IEnuemrable<DataItem>> Do() 
{
    ...
    foreach (var ele in await Functions.GetDataAsyncDo(ele.Id)) 
    {
        yield return ele; // compile time error, async method 
                          // cannot be used with yield return
    }

}

score:1

With C#8 and IAsyncEnumerable we can write this more naturally:

public static async IAsyncEnumerable<R> 
    SelectManyAsync<T, R>(this IEnumerable<T> ts, Func<T, Task<IEnumerable<R>>> func)
{
    foreach (var t in ts)
    {
        var rs = await func(t);
        foreach (var r in rs)
            yield return r;
    }
}

Note: Use async foreach(... to iterate over an IAsyncEnumerable

score:8

Select works because it will return an IEnumerable<Task<T>>, which can then be awaited with e.g. Task.WhenAll.

So, an easy workaround to this problem is:

IEnumerable<Task<IEnumerable<T>>> tasks = source.Select(GetNestedEnumerableTask);
IEnumerable<T>[] nestedResults = await Task.WhenAll(tasks);
IEnumerable<T> results = nestedResults.SelectMany(nr => nr);

score:58

This is an extension:

public static async Task<IEnumerable<T1>> SelectManyAsync<T, T1>(this IEnumerable<T> enumeration, Func<T, Task<IEnumerable<T1>>> func)
{
    return (await Task.WhenAll(enumeration.Select(func))).SelectMany(s => s);
}

That allows you to run:

var result = await myEnumerable.SelectManyAsync(c => Functions.GetDataAsync(c.Id));

Explanation: you have a list of tasks, each returns Task<IEnumerable<T>>. So you need to fire them all, then await all, and then squash the result via SelectMany.


Related Query