IEnumerable a IQueryable

0

Cześć, mam pytanie dotyczące interfejsów IEnumerable i IQueryable. Napisałem prosty program z zapytaniem do bay z użyciem tych dwóch interfejsów. Rozumiem, że główna różnica pomiędzy nimi jest to, że w przypadku IQueryable dane zostaną filtrowane na serwerze a w przypadku IEnumerable lokalnie. Postanowiłem jednak napisać do samo zapytanie z wykorzystniem tych dwóch interfejsów i obserwować róch na sieci.

            IEnumerable<byte?> employeeIEnumberable =
                dc.Adult_Census_Income_Binary_Classification_datasets.
                Select(p => p.age).Take(10);

            IQueryable<byte?> employeeIQueryable =
                dc.Adult_Census_Income_Binary_Classification_datasets.
                Select(p => p.age).Take(10);
            IEnumerable<byte?> employeeIEnumberable =
                dc.Adult_Census_Income_Binary_Classification_datasets.
                Select(p => p.age);
            employeeIEnumberable.Take(10);

            IQueryable<byte?> employeeIQueryable =
                dc.Adult_Census_Income_Binary_Classification_datasets.
                Select(p => p.age);
            employeeIQueryable.Take(10);

W pierwszym przypadku ruch na siecie w dwóch przypadkach jest taki sam. W drugim przypadku przy IEnumerable ruch ten jest znacznie większy, w przypadku IQueryable ruch pozostaje na niskim poziomie. I tutaj mam pytnie właściwie czemu w pierwszym przypadku ruch przy IEnumerable był na niskim poziomie skoro filtrowanie wyników przy użyciu tego interfejsu powinno odbyć się lokalnie?

1
uniqa napisał(a):

I tutaj mam pytnie właściwie czemu w pierwszym przypadku ruch przy IEnumerable był na niskim poziomie skoro filtrowanie wyników przy użyciu tego interfejsu powinno odbyć się lokalnie?

Bo w pierwszym przypadku korzystasz z IQueryable a dopiero po uzyskaniu wyników z bazy rzutujesz na IEnumerable.

2

IEnumerable<T> działa na linq-to-objects, IQueryable<T> to link-to-sql.
Oznacza to, że dopóki działasz na IQueryable<T> całość lambd ze wszystkich warunków, grupowań, sortowań itp. będzie przetłumaczona na sql. Z tego powodu też istnieją duże ograniczenia na to, co w takich lambdach może się znaleźć (zestaw metod, które możesz użyć, jest predefiniowany). Zaletą jest to, że przetwarzanie danych zrzucasz na bazę danych, czyli używasz młotka do wbijania gwoździ (jak zawsze istnieją wyjątki, czasem opłaca się pobrać więcej danych i obrabiać w kliencie, czyli tutaj w C#, ale żeby to stwierdzić, to i tak trzeba wygenerować zapytanie i przeanalizować jego plan wykonania), ponadto przesyłasz tylko tyle danych, ile trzeba.
Jeśli jakikolwiek wynik zapytania zrzutujesz na IEnumerable<T>, to każde (!) odwołanie do wyniku spowoduje zassanie z bazy wszystkich rekordów. Jeśli na IEnumerable<T> wykonasz podzapytanie (np. Take), to pobrane będą wszystkie rekordy określone w IEnumerable<T>, a dopiero na nich odbędzie się filtrowanie, sortowanie itp. Wbijasz wszystkie gwoździe, chociaż potrzebujesz tylko jeden.
Ponieważ IEnumerable<T> iteruje po danych przy każdym odwołaniu spowoduje to kolejne wykonania sql za nim stojącego. Z tego powodu nie polecam używania IEnumerable<T> w ogóle. Albo IQueryable<T>, albo ICollection<T>, albo IList<T>

[edit]
Nie odpowiedziałem na zadane pytanie ;-) Więc "czemu w pierwszym przypadku ruch przy IEnumerable był na niskim poziomie"? Bo rzutuwanie na IEnumerable odbywa się dopiero po wykonaniu Take(). Samo Take() wykonuje się na IQueryable.

1 użytkowników online, w tym zalogowanych: 0, gości: 1