Pracuję nad niektórymi elementami interfejsu API sieci Web przy użyciu Entity Framework 6 i jedną z moich metod kontrolera jest „Pobierz wszystko”, która oczekuje, że zawartość tabeli z mojej bazy danych zostanie odebrana jako IQueryable<Entity>
. W moim repozytorium zastanawiam się, czy istnieje jakiś korzystny powód, aby robić to asynchronicznie, ponieważ jestem nowy w używaniu EF z asynchronicznie.
Zasadniczo sprowadza się to do
public async Task<IQueryable<URL>> GetAllUrlsAsync()
{
var urls = await context.Urls.ToListAsync();
return urls.AsQueryable();
}
vs
public IQueryable<URL> GetAllUrls()
{
return context.Urls.AsQueryable();
}
Czy wersja asynchroniczna rzeczywiście przyniesie tutaj korzyści w zakresie wydajności, czy też poniosę niepotrzebne obciążenie, najpierw wykonując projekcję na Listę (używając asynchronicznego myślenia), a NASTĘPNIE przechodząc do IQueryable?
c#
entity-framework
async-await
Jesse Carter
źródło
źródło
Odpowiedzi:
Wydaje się, że problem polega na tym, że źle zrozumiałeś, jak działa async / await z Entity Framework.
Informacje o Entity Framework
Spójrzmy więc na ten kod:
i przykład użycia:
Co tu się dzieje?
IQueryable
obiekt (jeszcze nie uzyskujemy dostępu do bazy danych) za pomocąrepo.GetAllUrls()
IQueryable
obiekt z określonym warunkiem za pomocą.Where(u => <condition>
IQueryable
obiekt z określonym limitem stronicowania za pomocą.Take(10)
.ToList()
. NaszIQueryable
obiekt jest kompilowany do sql (jakselect top 10 * from Urls where <condition>
). Baza danych może korzystać z indeksów, serwer sql wysyła tylko 10 obiektów z bazy danych (nie wszystkie miliardy adresów URL przechowywane w bazie danych)Dobra, spójrzmy na pierwszy kod:
Z tego samego przykładu użycia otrzymaliśmy:
await context.Urls.ToListAsync();
.Informacje o async / await
Dlaczego preferowane jest użycie async / await? Spójrzmy na ten kod:
co się tutaj stało?
var stuff1 = ...
userId
var stuff2 = ...
userId
Spójrzmy więc na wersję asynchroniczną:
co się tutaj stało?
Właściwy sposób, aby to zrobić
Tak dobry kod tutaj:
Uwaga, niż musisz dodać
using System.Data.Entity
, aby użyć metodyToListAsync()
dla IQueryable.Zwróć uwagę, że jeśli nie potrzebujesz filtrowania, stronicowania i innych rzeczy, nie musisz pracować z
IQueryable
. Możesz po prostu używaćawait context.Urls.ToListAsync()
i pracować ze zmaterializowanymiList<Url>
.źródło
GetAllUrlsByUser
metodzie, nie musisz ustawiać go jako asynchronicznego. Po prostu zwróć zadanie i zapisz sobie niepotrzebną maszynę stanu przed wygenerowaniem przez kompilator.async
iawait
jeśli NIE robisz nic z listą. Niech dzwoni doawait
tego. Kiedy czekasz na wywołanie na tym etapiereturn await GetAllUrls().Where(u => u.User.Id == userId).ToListAsync();
, tworzysz dodatkową otokę asynchroniczną podczas dekompilacji zestawu i patrzenia na IL.W opublikowanym przykładzie jest ogromna różnica, pierwsza wersja:
To jest złe , w zasadzie tak
select * from table
, zwraca wszystkie wyniki do pamięci, a następnie stosujewhere
przeciw temu w kolekcji pamięci zamiast robićselect * from table where...
w bazie danych.Druga metoda nie trafi w rzeczywistości do bazy danych, dopóki zapytanie nie zostanie zastosowane do
IQueryable
(prawdopodobnie za pośrednictwem.Where().Select()
operacji w stylu linq , która zwróci tylko wartości db, które pasują do zapytania.Jeśli twoje przykłady były porównywalne,
async
wersja będzie zwykle nieco wolniejsza na żądanie, ponieważ na maszynie stanów jest więcej narzutów, które generuje kompilator, aby umożliwićasync
funkcjonalność.Jednak główną różnicą (i korzyścią) jest to, że
async
wersja umożliwia więcej jednoczesnych żądań, ponieważ nie blokuje wątku przetwarzania podczas oczekiwania na zakończenie operacji we / wy (zapytanie bazy danych, dostęp do pliku, żądanie sieciowe itp.).źródło
Krótko mówiąc,
IQueryable
ma na celu opóźnienie procesu RUN i najpierw zbudowanie wyrażenia w połączeniu z innymiIQueryable
wyrażeniami, a następnie interpretuje i uruchamia wyrażenie jako całość.Jednak
ToList()
metoda method (lub kilka takich metod) ma na celu natychmiastowe uruchamianie wyrażenia „tak jak jest”.Twoja pierwsza metoda (
GetAllUrlsAsync
) zostanie uruchomiona natychmiast, ponieważIQueryable
następuje po niejToListAsync()
metoda method. dlatego działa natychmiast (asynchronicznie) i zwraca kilkaIEnumerable
s.W międzyczasie Twoja druga metoda (
GetAllUrls
) nie zostanie uruchomiona. Zamiast tego zwraca wyrażenie, a CALLER tej metody jest odpowiedzialny za uruchomienie wyrażenia.źródło