Search code examples
c#linqlambdalinq-to-objects

How to use async lambda with SelectMany?


I'm getting the following error when trying to use an async lambda within IEnumerable.SelectMany:

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

The type arguments for method 'IEnumerable System.Linq.Enumerable.SelectMany(this IEnumerable, Func>)' cannot be inferred from the usage. Try specifying the type arguments explicitly

Where GetDataAsync is defined as:

public interface IFunctions {
    Task<IEnumerable<DataItem>> GetDataAsync(string itemId);
}

public class Functions : IFunctions {
    public async Task<IEnumerable<DataItem>> GetDataAsync(string itemId) {
        // return await httpCall();
    }
}

I guess because my GetDataAsync method actually returns a Task<IEnumerable<T>>. But why does Select work, surely it should throw the same error?

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

Is there any way around this?


Solution

  • 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
        }
    
    }