Вы должны знать разницу между запросом и его результатом. IQueryable
содержит все для выполнения запроса. Это не сам запрос, и создание IQueryable не выполняет запрос.
Если вы посмотрите более внимательно на операторы LINQ, вы увидите, что есть два типа: те, которые возвращают IQueryable
(и IEnumerable
), и те, которые возвращают List<TResult>
, TResults
, TKey
и т. Д., Все, что не IQueryable/IEnumerable
. Если возвращаемое значение равно IQueryable
, то мы говорим, что функция использует отложенное выполнение (или отложенное выполнение): Expression
для выполнения запроса создан, но запрос еще не выполнен.
Это имеет то преимущество, что вы можете объединять операторы LINQ без выполнения запроса для каждого оператора.
Запрос выполняется, когда вы запрашиваете IQueryable для получения перечислителя, и если вы начинаете перечисление, либо неявно с помощью foreach
, либо явно с использованием IQueryable.GetEnumerator()
и IEnumerator.MoveNext()
(которые также вызываются с помощью foreach
).
Так что, пока вы создаете запрос и возвращаете IQueryable, создавать Задачу бесполезно. Конкатенация оператора LINQ изменит только выражение IQueryable
, которое вам не нужно ждать.
Только если вы создадите функцию, которая будет фактически выполнять запрос, вам потребуется асинхронная версия: ToListAsync
, FirstOrDefaultAsync
, MaxAsync
и т. Д. Внутренне эти функции будут GetEnumerator
и MoveNextAsync
<- - это фактическая асинхронная функция </p>
Вывод: все ваши функции, которые обычно возвращают
IQueryable<...>
не требуется асинхронная версия, все функции, которые
для возврата фактических извлеченных данных требуется версия Async
Примеры. Асинхронизация не требуется: запрос не выполнен:
// Query customer addresses:
static IQueryable<Address> QueryAddresses(this IQueryable<Address> customers)
{
return customers.Select(customer => customer.Address);
}
Требуется асинхронность:
static async Task<List<Address>> FetchAddressesAsync (this IQueryable<Customer> customers)
{
var query = customers.QueryAddresses; // no query executed yet
return await query.ToListAsync(); // execute the query
// could of course be done in one statement
}
static async Task<Address> FetchAddressAsync(this.IQueryable<Customer> customers, int customerId)
{
var query = customers.Where(customer => customer.Id == customerId)
.FetchAddresses();
// no query executed yet!
// execute:
return await query.FirstOrDefaultAsync();
}
Использование:
int customerId = ...
using (var dbContext = new InvoiceContext())
{
Address fetchedCustomerAddress = await dbContext.Customers
.FetchAddressAsync(customerId);
}
В том редком случае, когда вам придется перечислять себя, вы будете ждать в MoveNextAsync
:
IQueryable<Customer> myCustomers = ...
IEnumerator<Customer> customerEnumerator = myCustomers.GetEnumerator();
while (await customerEnumerator.MoveNextAsync())
{
Customer customer = customerEnumerator.Current;
Process(customer);
}