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 foreachpętlę wewnątrz , chociaż można przejść IEnumeratortylko). 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.
IQueryablereprezentuje 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 IQueryableobiektó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ń) IQueryablewyrażenia są tłumaczone na natywne zapytania T-SQL . Nhibernaterobi 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 IQueryableobiekty 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, LINQfunkcją 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 IEnumerablena Listy (indeksatory i tym podobne). Tak więc radzę używać IQueryabletylko w repozytoriach i IEnumerable gdziekolwiek indziej w kodzie. Nie mówiąc o obawy testowalności że IQueryablezał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.