问题
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?
回答1:
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
}
}
回答2:
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.
回答3:
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);
来源:https://stackoverflow.com/questions/33495617/how-to-use-async-lambda-with-selectmany