Как подождать метод в запросе Linq
Попытка использовать ключевое слово await
в запросе LINQ
, и я получаю следующее:
Оператор 'wait' может использоваться только в выражении запроса в первом выражении коллекции исходного предложения from from или внутри выражения коллекции предложения 'join'
Пример кода:
var data = (from id in ids
let d = await LoadDataAsync(id)
select d);
Невозможно ли ожидать чего-либо в запросе LINQ
или его нужно структурировать по-другому?
Ответы
Ответ 1
LINQ имеет очень ограниченную поддержку async
/await
. Для LINQ-to-objects единственная действительно полезная операция, которую я знаю, - это сделать Select
с делегатом async
(что приводит к последовательности задач).
List<T> data = new List<T>();
foreach (var id in ids)
data.Add(await LoadDataAsync(id));
Если вы можете сделать LoadDataAsync
параллельно безопасно, ваш пример можно переписать как:
T[] data = await Task.WhenAll(ids.Select(id => LoadDataAsync(id)));
Ответ 2
Вы можете определить некоторые операции async linq самостоятельно (для linq для объектов):
например: вы можете написать собственный метод расширения WhereAsync:
public static async Task<IEnumerable<T>> WhereAsync<T>(
this IEnumerable<T> target, Func<T, Task<bool>> predicateAsync)
{
var tasks = target.Select(async x => new { Predicate = await predicateAsync(x).ConfigureAwait(false), Value = x }).ToArray();
var results = await Task.WhenAll(tasks).ConfigureAwait(false);
return results.Where(x => x.Predicate).Select(x => x.Value);
}
И используйте его так:
var ints = new List<int> { 1, 2, 3 };
var smallInts = await ints.WhereAsync(IsSmallIntAsync);
Ответ 3
Используя реактивные расширения, можно обрабатывать результаты запроса linq асинхронно следующим образом:
(from d in ids
select LoadDataAsync(d).ToObservable()).Merge()
Это дает вам наблюдаемый поток, на который вы можете ответить различными способами. Например, вы можете затем .Buffer результаты в список с таймаутом.
Вышеупомянутое, по существу, говорит "для каждого d в ids, применяйте к нему асинхронную функцию, которая дает задание для каждого d и рассматривает это как наблюдаемое для отдельного результата (ToObservable) и обрабатывает все эти наблюдаемые вместе как один наблюдаемый поток (Merge)