Я работаю над некоторыми вещами веб-API, используя Entity Framework 6, и один из моих методов контроллера - это «Получить все», который ожидает получить содержимое таблицы из моей базы данных как IQueryable<Entity>
. В моем репозитории мне интересно, есть ли какие-либо преимущества для того, чтобы делать это асинхронно, поскольку я новичок в использовании EF с async.
В основном это сводится к
public async Task<IQueryable<URL>> GetAllUrlsAsync()
{
var urls = await context.Urls.ToListAsync();
return urls.AsQueryable();
}
против
public IQueryable<URL> GetAllUrls()
{
return context.Urls.AsQueryable();
}
Будет ли асинхронная версия действительно давать здесь преимущества в производительности, или я несу ненужные накладные расходы, сначала проецируя на List (используя async, заметьте), а ЗАТЕМ перехожу к IQueryable?
c#
entity-framework
async-await
Джесси Картер
источник
источник
Ответы:
Проблема, похоже, в том, что вы неправильно поняли, как async / await работают с Entity Framework.
О Entity Framework
Итак, посмотрим на этот код:
и пример его использования:
Что там происходит?
IQueryable
объект (пока не получаем доступ к базе данных), используяrepo.GetAllUrls()
IQueryable
объект с заданным условием, используя.Where(u => <condition>
IQueryable
объект с указанным пределом разбиения на страницы, используя.Take(10)
.ToList()
. НашIQueryable
объект скомпилирован в sql (вродеselect top 10 * from Urls where <condition>
). И база данных может использовать индексы, sql-сервер отправляет вам только 10 объектов из вашей базы данных (не все миллиарды URL-адресов, хранящиеся в базе данных)Хорошо, посмотрим на первый код:
На том же примере использования мы получили:
await context.Urls.ToListAsync();
.О async / await
Почему предпочтительнее использовать async / await? Посмотрим на этот код:
Что здесь происходит?
var stuff1 = ...
userId
var stuff2 = ...
userId
Итак, давайте посмотрим на его асинхронную версию:
Что здесь происходит?
Правильный способ сделать это
Итак, хороший код здесь:
Обратите внимание, что вы должны добавить
using System.Data.Entity
, чтобы использовать методToListAsync()
для IQueryable.Обратите внимание: если вам не нужны фильтрация, разбивка по страницам и прочее, вам не нужно работать
IQueryable
. Вы можете просто использоватьawait context.Urls.ToListAsync()
и работать с материализованнымList<Url>
.источник
GetAllUrlsByUser
методе, вам не нужно делать его асинхронным. Просто верните Задачу и избавьте себя от ненужного конечного автомата от генерации компилятором.async
и,await
если вы ничего НЕ делаете со списком. Пусть звонящий кawait
нему. Ожидая вызова на этом этапе,return await GetAllUrls().Where(u => u.User.Id == userId).ToListAsync();
вы создаете дополнительную асинхронную оболочку, когда декомпилируете сборку и смотрите на IL.В опубликованном вами примере первой версии есть огромная разница:
Это плохо , в основном это так
select * from table
, возвращает все результаты в память, а затем применяетwhere
против них в сборе памяти, а неselect * from table where...
против базы данных.Второй метод фактически не попадет в базу данных до тех пор, пока запрос не будет применен к
IQueryable
(возможно, через.Where().Select()
операцию в стиле linq, которая вернет только значения db, которые соответствуют запросу.Если ваши примеры были сопоставимы,
async
версия обычно будет немного медленнее на запрос, так как в конечном автомате, который генерирует компилятор, больше накладных расходов, чтобы обеспечитьasync
функциональность.Однако основное отличие (и преимущество) заключается в том, что
async
версия позволяет выполнять больше одновременных запросов, поскольку не блокирует поток обработки, пока он ожидает завершения ввода-вывода (запрос базы данных, доступ к файлу, веб-запрос и т. Д.).источник
Короче говоря,
IQueryable
он предназначен для того, чтобы отложить процесс RUN и сначала построить выражение вместе с другимиIQueryable
выражениями, а затем интерпретировать и запускать выражение в целом.Но
ToList()
метод (или несколько подобных ему методов) предназначен для мгновенного выполнения выражения «как есть».Ваш первый метод (
GetAllUrlsAsync
) будет запущен немедленно, потому чтоIQueryable
за ним следуетToListAsync()
метод. следовательно, он выполняется мгновенно (асинхронно) и возвращает кучуIEnumerable
s.Между тем ваш второй метод (
GetAllUrls
) не запустится. Вместо этого он возвращает выражение, и CALLER этого метода отвечает за выполнение выражения.источник