Wiele już powiedziano, ale wracając do korzeni, w bardziej techniczny sposób:
IEnumerable
to zbiór obiektów w pamięci, które można wyliczyć - sekwencja w pamięci, która umożliwia iterację (ułatwia foreach
pętlę wewnątrz , chociaż można przejść IEnumerator
tylko). Pozostają w pamięci jak są.
IQueryable
jest drzewem wyrażeń, które w pewnym momencie zostanie przetłumaczone na coś innego, z możliwością wyliczenia końcowego wyniku . Myślę, że właśnie to dezorientuje większość ludzi.
Mają oczywiście różne konotacje.
IQueryable
reprezentuje drzewo wyrażeń (zapytanie, po prostu), które zostanie przetłumaczone na coś innego przez podstawowego dostawcę zapytań, gdy tylko zostaną wywołane interfejsy API wydania, takie jak funkcje agregujące LINQ (Sum, Count, itp.) lub ToList [Array, Dictionary ,. ..]. I IQueryable
obiektów również wdrożyć IEnumerable
, IEnumerable<T>
tak że jeśli stanowią one kwerendy wynik tego zapytania może być powtórzyć. Oznacza to, że IQueryable nie muszą być tylko zapytaniami. Właściwy termin to drzewa ekspresyjne .
Teraz, w jaki sposób te wyrażenia są wykonywane i do czego się zwracają, wszystko zależy od tak zwanych dostawców zapytań (wykonawców wyrażeń, o których możemy myśleć).
W świecie Entity Framework (czyli mistycznym źródłowym źródle danych lub dostawcy zapytań) IQueryable
wyrażenia są tłumaczone na natywne zapytania T-SQL . Nhibernate
robi z nimi podobne rzeczy. Możesz napisać własny zgodnie z pojęciami całkiem dobrze opisanymi w LINQ: Na przykład budowanie łącza do IQueryable Provider , a możesz chcieć mieć niestandardowy interfejs API do wysyłania zapytań dla usługi dostawcy magazynu produktów.
Zasadniczo IQueryable
obiekty są konstruowane przez cały czas, dopóki ich jawnie nie zwolnimy i nie powiemy systemowi, aby przepisał je na SQL lub cokolwiek innego i przesłał łańcuch wykonawczy do dalszego przetwarzania.
Jakby w celu odroczenia wykonania, LINQ
funkcją jest zachowanie schematu drzewa wyrażeń w pamięci i wysyłanie go do wykonania tylko na żądanie, za każdym razem, gdy niektóre interfejsy API są wywoływane w sekwencji (ten sam Count, ToList itp.).
Właściwe użycie obu zależy w dużej mierze od zadań, które stoją przed konkretnym przypadkiem. W przypadku dobrze znanego wzorca repozytorium osobiście decyduję się na zwrot IList
, to znaczy IEnumerable
na Listy (indeksatory i tym podobne). Tak więc radzę używać IQueryable
tylko w repozytoriach i IEnumerable gdziekolwiek indziej w kodzie. Nie mówiąc o obawy testowalności że IQueryable
załamuje się i niszczy separacji obawy co do zasady. Jeśli zwrócisz wyrażenie z repozytoriów, konsumenci mogą bawić się warstwą trwałości, jak chcieliby.
Mały dodatek do bałaganu :) (z dyskusji w komentarzach)) Żadne z nich nie są obiektami w pamięci, ponieważ same w sobie nie są prawdziwymi typami, są markerami typu - jeśli chcesz zagłębić się tak głęboko. Ale ma sens (i dlatego nawet MSDN ujął to w ten sposób) myśleć o IEnumerables jako kolekcjach w pamięci, podczas gdy IQueryables o drzewach wyrażeń. Chodzi o to, że interfejs IQueryable dziedziczy interfejs IEnumerable, więc jeśli reprezentuje zapytanie, wyniki tego zapytania można wyliczyć. Wyliczenie powoduje wykonanie drzewa wyrażeń powiązanego z obiektem IQueryable. Tak więc w rzeczywistości nie można wywołać żadnego elementu IEnumerable bez posiadania obiektu w pamięci. Dostanie się tam, jeśli tak zrobisz, jeśli nie będzie puste. IQueryables to tylko zapytania, a nie dane.