Как использовать асинхронную лямбда с помощью SelectMany?
Я получаю следующую ошибку при попытке использовать async
lambda внутри IEnumerable.SelectMany
:
var result = myEnumerable.SelectMany(async (c) => await Functions.GetDataAsync(c.Id));
Аргументы типа для метода 'IEnumerable System.Linq.Enumerable.SelectMany(это IEnumerable, Func > ) 'не может быть выведенных из использования. Попробуйте явно указать аргументы типа
Где GetDataAsync
определяется как:
public interface IFunctions {
Task<IEnumerable<DataItem>> GetDataAsync(string itemId);
}
public class Functions : IFunctions {
public async Task<IEnumerable<DataItem>> GetDataAsync(string itemId) {
// return await httpCall();
}
}
Я думаю, потому что мой метод GetDataAsync
фактически возвращает Task<IEnumerable<T>>
. Но почему работает Select
, наверняка, он должен выбросить ту же ошибку?
var result = myEnumerable.Select(async (c) => await Functions.GetDataAsync(c.Id));
Есть ли способ обойти это?
Ответы
Ответ 1
выражение async lambda не может быть преобразовано в простой Func<TSource, TResult>
.
Итак, выберите многие, которые нельзя использовать. Вы можете работать в синхронизированном контексте:
myEnumerable.Select(c => Functions.GetDataAsync(c.Id)).SelectMany(task => task.Result);
или
List<DataItem> result = new List<DataItem>();
foreach (var ele in myEnumerable)
{
result.AddRange(await Functions.GetDataAsyncDo(ele.Id));
}
Вы не можете использовать yield return
- это по дизайну. 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
Это расширение:
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);
}
Это позволяет запускать:
var result = await myEnumerable.SelectManyAsync(c => Functions.GetDataAsync(c.Id));
Объяснение: у вас есть список задач, каждый возвращает Task<IEnumerable<T>>
. Поэтому вам нужно уволить их всех, а затем ждать всех, а затем раздавать результат с помощью SelectMany.
Ответ 3
Select
работает, потому что он вернет IEnumerable<Task<T>>
, который затем можно ждать, например. Task.WhenAll
.
Итак, простой способ решения этой проблемы:
IEnumerable<Task<IEnumerable<T>>> tasks = source.Select(GetNestedEnumerableTask);
IEnumerable<T>[] nestedResults = await Task.WhenAll(tasks);
IEnumerable<T> results = nestedResults.SelectMany(nr => nr);