Znam pewne różnice między LINQ a jednostkami i LINQ do obiektów, które pierwsza implementuje, IQueryable
a druga implementuje, IEnumerable
a zakres moich pytań mieści się w EF 5.
Moje pytanie brzmi: jaka jest różnica techniczna tych 3 metod? Widzę, że w wielu sytuacjach wszystkie działają. Widzę też użycie ich kombinacji .ToList().AsQueryable()
.
Co dokładnie oznaczają te metody?
Czy jest jakiś problem z wydajnością lub coś, co prowadziłoby do używania jednego nad drugim?
Dlaczego miałoby się na przykład używać
.ToList().AsQueryable()
zamiast.AsQueryable()
?
Odpowiedzi:
Jest wiele do powiedzenia na ten temat. Pozwól mi skupić się na
AsEnumerable
iAsQueryable
i wzmiankaToList()
po drodze.Co robią te metody?
AsEnumerable
i odpowiednioAsQueryable
obsadzić lub przekonwertować naIEnumerable
lubIQueryable
. Mówię obsadę lub konwersję bez podania przyczyny:Gdy obiekt źródłowy już implementuje interfejs docelowy, sam obiekt źródłowy jest zwracany, ale rzutowany na interfejs docelowy. Innymi słowy: typ nie jest zmieniany, ale typem kompilacji jest.
Gdy obiekt źródłowy nie implementuje interfejsu docelowego, obiekt źródłowy jest konwertowany na obiekt, który implementuje interfejs docelowy. Tak więc zarówno typ, jak i typ kompilacji są zmieniane.
Pokażę to na kilku przykładach. Mam tę małą metodę, która zgłasza typ czasu kompilacji i rzeczywisty typ obiektu ( dzięki uprzejmości Jona Skeeta ):
Spróbujmy dowolnego linq-to-sql
Table<T>
, który implementujeIQueryable
:Wynik:
Widzisz, że sama klasa tabeli jest zawsze zwracana, ale jej reprezentacja zmienia się.
Teraz obiekt, który implementuje
IEnumerable
, a nieIQueryable
:Wyniki:
Tu jest.
AsQueryable()
przekształcił tablicę w obiektEnumerableQuery
, który „reprezentujeIEnumerable<T>
kolekcję jakoIQueryable<T>
źródło danych”. (MSDN).Jakie jest zastosowanie?
AsEnumerable
jest często używany do przełączania z dowolnejIQueryable
implementacji na LINQ na obiekty (L2O), głównie dlatego, że ta pierwsza nie obsługuje funkcji, które ma L2O. Aby uzyskać więcej informacji, zobacz Jaki jest wpływ AsEnumerable () na jednostkę LINQ? .Na przykład w zapytaniu Entity Framework możemy użyć tylko ograniczonej liczby metod. Więc jeśli na przykład musimy użyć jednej z naszych własnych metod w zapytaniu, zwykle napisalibyśmy coś takiego
ToList
- który zamienia anIEnumerable<T>
na aList<T>
- jest również często używany do tego celu. Zaletą używaniaAsEnumerable
vs.ToList
jest to, żeAsEnumerable
nie wykonuje zapytania.AsEnumerable
zachowuje odroczone wykonanie i nie tworzy często bezużytecznej listy pośredniej.Z drugiej strony, gdy pożądane jest wymuszone wykonanie zapytania LINQ,
ToList
można to zrobić.AsQueryable
może służyć do tego, aby kolekcja wymienna akceptowała wyrażenia w instrukcjach LINQ. Zobacz więcej szczegółów: Czy naprawdę potrzebuję używać AsQueryable () w kolekcji? .Uwaga na temat nadużywania substancji!
AsEnumerable
działa jak narkotyk. Jest to szybka poprawka, ale kosztuje i nie rozwiązuje podstawowego problemu.W wielu odpowiedziach na przepełnienie stosu widzę, że ludzie starają
AsEnumerable
się naprawić prawie każdy problem z nieobsługiwanymi metodami w wyrażeniach LINQ. Ale cena nie zawsze jest jasna. Na przykład, jeśli to zrobisz:... wszystko jest starannie przetłumaczone na instrukcję SQL, która filtruje (
Where
) i projekty (Select
). Oznacza to, że zarówno długość, jak i szerokość zestawu wyników SQL są odpowiednio zmniejszone.Załóżmy teraz, że użytkownicy chcą widzieć tylko datę
CreateDate
. W Entity Framework szybko odkryjesz, że ...... nie jest obsługiwany (w momencie pisania). Ach, na szczęście jest
AsEnumerable
poprawka:Pewnie, pewnie działa. Ale wciąga całą tabelę do pamięci, a następnie stosuje filtr i projekcje. Cóż, większość ludzi jest wystarczająco inteligentna, aby zrobić
Where
pierwsze:Ale nadal wszystkie kolumny są pobierane jako pierwsze, a projekcja odbywa się w pamięci.
Prawdziwa poprawka to:
(Ale to wymaga trochę więcej wiedzy ...)
Czego NIE robią te metody?
Przywróć możliwości IQueryable
Teraz ważne zastrzeżenie. Kiedy to zrobisz
skończysz z obiektem źródłowym reprezentowanym jako
IQueryable
. (Ponieważ obie metody tylko rzutują i nie konwertują).Ale kiedy to zrobisz
jaki będzie wynik?
Select
ProdukujeWhereSelectEnumerableIterator
. Jest to wewnętrzna klasa .Net, która implementujeIEnumerable
, a nieIQueryable
. Tak więc nastąpiła konwersja na inny typ i kolejneAsQueryable
nigdy nie mogą już zwrócić oryginalnego źródła.Konsekwencją tego jest, że używanie
AsQueryable
to nie sposób magicznie wstrzyknąć dostawca zapytania z jego specyficznymi cechami w produkt przeliczalny. Załóżmy, że takMiejsce, w którym warunek nigdy nie zostanie przetłumaczony na SQL.
AsEnumerable()
następnie instrukcje LINQ definitywnie przerywają połączenie z dostawcą zapytań w ramach encji.Celowo pokazuję ten przykład, ponieważ widziałem tutaj pytania, w których ludzie na przykład próbują „wprowadzić”
Include
funkcje do kolekcji, dzwoniącAsQueryable
. Kompiluje się i działa, ale nic nie robi, ponieważ obiekt bazowy nie maInclude
już implementacji.Wykonać
Zarówno
AsQueryable
iAsEnumerable
nie wykonują (lub enumerate ) obiekt źródłowy. Zmieniają tylko swój typ lub reprezentację. Oba dotyczyły interfejsówIQueryable
iIEnumerable
są niczym innym jak „wyliczeniem, które się czeka”. Nie są one wykonywane, dopóki nie zostaną do tego zmuszone, na przykład, jak wspomniano powyżej, przez telefonToList()
.Oznacza to, że Wykonywanie
IEnumerable
uzyskać dzwoniącAsEnumerable
naIQueryable
obiekcie, wykona instrumentu bazowegoIQueryable
. Kolejne wykonanieIEnumerable
testamentu ponownie wykonaIQueryable
. Co może być bardzo drogie.Konkretne implementacje
Do tej pory było to tylko o
Queryable.AsQueryable
iEnumerable.AsEnumerable
metod rozszerzenie. Ale oczywiście każdy może pisać metody instancji lub metody rozszerzeń o tych samych nazwach (i funkcjach).W rzeczywistości powszechnym przykładem konkretnej
AsEnumerable
metody rozszerzenia jestDataTableExtensions.AsEnumerable
.DataTable
nie implementujeIQueryable
lubIEnumerable
, więc standardowe metody rozszerzenia nie mają zastosowania.źródło
AsQueryable()
często opiera się na błędnym przekonaniu. Pozwolę sobie jednak chwilę gotować z tyłu głowy i zobaczę, czy mogę dodać więcej informacji na ten temat.Notować()
AsEnumerable ()
Func<TSource, bool>
AsQueryable ()
Expression<Func<TSource, bool>>
AsQueryable()
Zwykle działa więc znacznie szybciej niżAsEnumerable()
na początku generuje T-SQL, który obejmuje wszystkie warunki w Linq.źródło
ToList () będzie wszystkim w pamięci i wtedy będziesz nad nim pracował. więc ToList (). gdzie (zastosuj jakiś filtr) jest wykonywany lokalnie. AsQueryable () wykona wszystko zdalnie, tzn. Filtr zostanie wysłany do bazy danych w celu zastosowania. Queryable nic nie robi, dopóki go nie wykonasz. ToList, jednak wykonuje się natychmiast.
Spójrz także na tę odpowiedź Dlaczego warto używać AsQueryable () zamiast List ()? .
EDYCJA: Również w twoim przypadku po wykonaniu ToList (), każda kolejna operacja jest lokalna, w tym AsQueryable (). Nie możesz przełączyć się na zdalne po uruchomieniu lokalnego. Mam nadzieję, że dzięki temu jest to trochę jaśniejsze.
źródło
Wystąpił zły wynik na poniższym kodzie.
Naprawiono za pomocą
W przypadku IQueryable, pozostań w IQueryable, jeśli to możliwe, staraj się nie używać jak IEnumerable.
Aktualizacja . Dzięki Gertowi Arnoldowi można to jeszcze bardziej uprościć jednym wyrażeniem .
źródło