Jaka jest różnica między IQueryable <T> a IEnumerable <T>?

Odpowiedzi:

258

Przede wszystkim, rozciąga się interfejs, więc coś można zrobić z „zwykły” , można również zrobić z .IQueryable<T> IEnumerable<T>IEnumerable<T>IQueryable<T>

IEnumerable<T>tylko ma GetEnumerator()metody, która zwraca Enumerator<T>na której można wywołać jego MoveNext()metodę iterację sekwencji T .

To IQueryable<T>, czego IEnumerable<T> nie ma, to w szczególności dwie właściwości - jedna, która wskazuje na dostawcę zapytań (np. Dostawca LINQ na SQL), a druga wskazuje na wyrażenie zapytania reprezentujące IQueryable<T>obiekt jako abstrakcyjne drzewo składni, które można przenosić w czasie wykonywania. zrozumiałe dla danego dostawcy zapytań (w przeważającej części nie można podać wyrażenia LINQ do SQL dostawcy LINQ do podmiotu bez zgłoszenia wyjątku).

Wyrażenie może być po prostu stałym wyrażeniem samego obiektu lub bardziej złożonym drzewem złożonego zestawu operatorów zapytań i operandów. Dostawca IQueryProvider.Execute()lub IQueryProvider.CreateQuery()metody zapytania są wywoływane z przekazanym do niego wyrażeniem , a następnie IQueryablezwracany jest odpowiednio wynik zapytania lub inny .

Mark Cidade
źródło
8
Czy korzystanie z AsQueryable ma jakieś zalety?
Jordan
6
Jedyną korzyścią jest wygoda. AsQueryable()po prostu rzutuje wyliczenie na kwerendę i zwraca ją, jeśli implementuje IQueryableinterfejs, w przeciwnym razie zawija go w ConstantExpression, do którego odwołuje się zwrócony EnumerableQueryobiekt.
Mark Cidade
3
Powiązane pytanie: IEnumerable <T> i IQueryable <T> wyjaśnienie?
Christopher Stevenson
1
Warto przeczytać ten artykuł
JenonD,
@JenonD link nie działa, oto waybackmachine: web.archive.org/web/20160904162931/http://www.dotnet-tricks.com/…
majjam
201

Podstawowa różnica polega na tym, że operatory LINQ do IQueryable<T>pobierania Expressionobiektów zamiast delegatów, co oznacza, że ​​otrzymywana przez nie niestandardowa logika zapytań, np. Predykat lub selektor wartości, ma postać drzewa wyrażeń zamiast delegata do metody.

  • IEnumerable<T> jest świetny do pracy z sekwencjami, które są iterowane w pamięci, ale
  • IQueryable<T> pozwala na brak pamięci, takie jak zdalne źródło danych, takie jak baza danych lub usługa internetowa.

Wykonanie zapytania:

  • W przypadku, gdy wykonanie zapytania będzie wykonywane „w toku” , zwykle wszystko, czego potrzeba, to kod (jako kod) do wykonania każdej części zapytania.

  • Tam, gdzie wykonanie zostanie wykonane poza procesem , logika zapytania musi być reprezentowana w danych, tak aby dostawca LINQ mógł przekonwertować go na odpowiednią formę do wykonania braku pamięci - niezależnie od tego, czy jest to zapytanie LDAP, SQL lub cokolwiek innego.

Więcej w:

http://www.codeproject.com/KB/cs/646361/WhatHowWhere.jpg

VonC
źródło
14
Podoba mi się wzmianka o operacjach braku pamięci. Wyjaśnia prawdziwą praktyczną różnicę w użyciu.
Ishmael Smyrnow
2
Zamiast brać delegatów jako parametry takie jak IEnumerable <T> Tam, gdzie robi to metoda, IQueryable <T> przyjmuje parametry wyrażenia <TDelegate>. Nie przekazujemy kodu do IQueryable <T>, przekazujemy drzewa wyrażeń (kod jako dane) do analizy przez dostawcę LINQ. C # 3.0 i LINQ
Lu55
coś jest nie tak z twoim linkiem do obrazu, z jakiegoś powodu nie wyświetla się w treści postu codeproject.com/KB/cs/646361/WhatHowWhere.jpg
jbooker
@joey Jednak widzę ten obraz w porządku w tej odpowiedzi: i.stack.imgur.com/2DAqv.jpg
VonC
190

To fajne wideo na youtube, które pokazuje, jak różne są te interfejsy, warte obejrzenia.

Poniżej znajduje się długa opisowa odpowiedź na to pytanie.

Pierwszą ważną kwestią do zapamiętania jest IQueryabledziedziczenie interfejsu IEnumerable, więc cokolwiek IEnumerablemoże zrobić, IQueryablemoże również zrobić.

wprowadź opis zdjęcia tutaj

Istnieje wiele różnic, ale omówmy jedną wielką różnicę, która stanowi największą różnicę. IEnumerableInterfejs jest przydatny, gdy twoja kolekcja jest ładowana za pomocą LINQlub Entity Framework i chcesz zastosować filtr do kolekcji.

Rozważ poniższy prosty kod, który wykorzystuje się IEnumerablew ramach encji. Używa Wherefiltra, aby uzyskać rekordy, których EmpIdjest 2.

EmpEntities ent = new EmpEntities();
IEnumerable<Employee> emp = ent.Employees; 
IEnumerable<Employee> temp = emp.Where(x => x.Empid == 2).ToList<Employee>();

To, gdzie filtr jest wykonywany po stronie klienta, gdzie IEnumerableznajduje się kod. Innymi słowy wszystkie dane są pobierane z bazy danych, a następnie na jego klient skanuje i pobiera rekord EmpIdjest 2.

wprowadź opis zdjęcia tutaj

Ale teraz patrz poniższy kod zmieniliśmy IEnumerablesię IQueryable. Tworzy zapytanie SQL po stronie serwera i tylko niezbędne dane są wysyłane po stronie klienta.

EmpEntities ent = new EmpEntities();
IQueryable<Employee> emp = ent.Employees;
IQueryable<Employee> temp =  emp.Where(x => x.Empid == 2).ToList<Employee>();

wprowadź opis zdjęcia tutaj

Różnica między IQueryablei IEnumerabledotyczy tego, gdzie wykonywana jest logika filtru. Jeden wykonuje po stronie klienta, a drugi w bazie danych.

Więc jeśli pracujesz tylko z gromadzeniem danych w pamięci, IEnumerableto dobry wybór, ale jeśli chcesz zapytać o zbieranie danych, które jest połączone z bazą danych, `IQueryable jest lepszym wyborem, ponieważ zmniejsza ruch w sieci i wykorzystuje moc języka SQL.

Shivprasad Koirala
źródło
Cześć, wielkie dzięki za wspaniałe wyjaśnienia. Rozumiem, dlaczego IQueryable jest lepszym wyborem dla danych „poza pamięcią”, ale staram się zrozumieć, dlaczego IEnumerable jest lepszy dla danych „w pamięci”? Nadal uważam, że lepiej jest uzyskać odfiltrowane dane niż uzyskać dane i zastosować filtr.
Imad
kiedy jest „w pamięci”, to tak naprawdę nie ma znaczenia. dane są już w pamięci, nie ma znaczenia, kiedy dokładnie są filtrowane.
Roni Axelrad
@Imad Na przykład rzutowanie danych poprzez konwersję encji DateTimeOffset na DateTime nie jest możliwe do wykonania przy użyciu IQueryable lub EntityFunctions. Najlepszym sposobem jest AsEnumerable () obiekt encji, a następnie Select () w viewmodel, który zawiera DateTime. Ten proces wykorzystuje funkcje w pamięci.
Jeff Li
57

IEnumerable: IEnumerable najlepiej nadaje się do pracy z kolekcją w pamięci (lub zapytaniami lokalnymi). IEnumerable nie przemieszcza się między elementami, jest tylko kolekcją do przodu.

IQueryable: IQueryable najlepiej nadaje się do zdalnego źródła danych, takiego jak baza danych lub usługa internetowa (lub zdalne zapytania). IQueryable to bardzo potężna funkcja, która umożliwia szereg interesujących scenariuszy odroczonego wykonania (takich jak zapytania stronicowania i oparte na kompozycji).

Więc jeśli musisz po prostu iterować kolekcję w pamięci, użyj IEnumerable, jeśli potrzebujesz wykonać dowolną manipulację z kolekcją, taką jak Zbiór danych i inne źródła danych, użyj IQueryable

Talha
źródło
3
Czy IEnumerable nie używa odroczonego wykonania?
Ian Warburton,
3
Odroczone wykonanie jest dostępne zarówno w IEnumerable, jak i IQueryable. Dalsze interesujące informacje można znaleźć tutaj: stackoverflow.com/questions/2876616/...
Ignacio Hagopian
18

Innymi słowy, inną istotną różnicą jest to, że IEnumerable wykonuje zapytanie selekcyjne po stronie serwera, ładuje dane do pamięci po stronie klienta, a następnie filtruje dane, podczas gdy IQueryable wykonuje zapytanie selekcyjne po stronie serwera ze wszystkimi filtrami.

W
źródło
17

W prawdziwym życiu, jeśli używasz ORM, takiego jak LINQ-to-SQL

  • Jeśli utworzysz IQueryable, zapytanie może zostać przekonwertowane na SQL i uruchomione na serwerze bazy danych
  • Jeśli utworzysz IEnumerable, wszystkie wiersze zostaną wciągnięte do pamięci jako obiekty przed uruchomieniem zapytania.

W obu przypadkach, jeśli nie nazywamy ToList()lub ToArray()wówczas zapytanie wykonywane za każdym razem, gdy jest używany, więc, powiedzmy, masz IQueryable<T>i wypełnić 4 Wykaz pól od niego, wtedy zapytanie zostanie uruchomione na bazie 4-krotnie.

Również jeśli rozszerzysz swoje zapytanie:

q.Where(x.name = "a").ToList()

Następnie z IQueryable wygenerowany SQL będzie zawierał „gdzie nazwa =„ a ”, ale przy IEnumerable wiele innych ról zostanie wycofanych z bazy danych, a następnie sprawdzenie .x.name =„ a ”zostanie wykonane przez .NET.

Ian Ringrose
źródło
10

IEnumerable odnosi się do kolekcji, ale IQueryable jest tylko zapytaniem, które zostanie wygenerowane w drzewie wyrażeń. Uruchomimy to zapytanie, aby uzyskać dane z bazy danych.

seyed
źródło
8

Poniższy mały test może pomóc zrozumieć jeden aspekt różnicy między IQueryable<T>i IEnumerable<T>. Powtórzyłem tę odpowiedź z tego postu, w którym próbowałem dodać poprawki do postu innej osoby

Utworzyłem następującą strukturę w DB (skrypt DDL):

CREATE TABLE [dbo].[Employee]([PersonId] [int] NOT NULL PRIMARY KEY,[Salary] [int] NOT NULL)

Oto skrypt wstawiania rekordów (skrypt DML):

INSERT INTO [EfTest].[dbo].[Employee] ([PersonId],[Salary])VALUES(1, 20)
INSERT INTO [EfTest].[dbo].[Employee] ([PersonId],[Salary])VALUES(2, 30)
INSERT INTO [EfTest].[dbo].[Employee] ([PersonId],[Salary])VALUES(3, 40)
INSERT INTO [EfTest].[dbo].[Employee] ([PersonId],[Salary])VALUES(4, 50)
INSERT INTO [EfTest].[dbo].[Employee] ([PersonId],[Salary])VALUES(5, 60)
GO

Teraz moim celem było po prostu uzyskanie 2 najlepszych rekordów z Employeetabeli w bazie danych. Dodałem element ADO.NET Entity Data Model do mojej aplikacji konsoli wskazując na Employeetabelę w mojej bazie danych i zacząłem pisać zapytania LINQ.

Kod trasy IQueryable :

using (var efContext = new EfTestEntities())
{
    IQueryable<int> employees = from e in efContext.Employees  select e.Salary;
    employees = employees.Take(2);

    foreach (var item in employees)
    {
        Console.WriteLine(item);
    }
}

Kiedy zacząłem uruchamiać ten program, rozpocząłem także sesję profilowania zapytań SQL na mojej instancji SQL Server i oto podsumowanie wykonania:

  1. Całkowita liczba wysłanych zapytań: 1
  2. Tekst zapytania: SELECT TOP (2) [c].[Salary] AS [Salary] FROM [dbo].[Employee] AS [c]

Po prostu IQueryablejest wystarczająco inteligentny, aby zastosować Top (2)klauzulę po stronie samego serwera bazy danych, dzięki czemu łączy tylko 2 z 5 rekordów. Dalsze filtrowanie w pamięci nie jest wcale wymagane po stronie komputera klienckiego.

Kod dla IEnumerable route :

using (var efContext = new EfTestEntities())
{
    IEnumerable<int> employees = from e in efContext.Employees  select e.Salary;
    employees = employees.Take(2);

    foreach (var item in employees)
    {
        Console.WriteLine(item);
    }
}

Podsumowanie wykonania w tym przypadku:

  1. Całkowita liczba wysłanych zapytań: 1
  2. Tekst zapytania przechwycony w programie profilującym SQL: SELECT [Extent1].[Salary] AS [Salary] FROM [dbo].[Employee] AS [Extent1]

Teraz IEnumerableprzyniesiono wszystkie 5 rekordów obecnych w Salarytabeli, a następnie przeprowadzono filtrowanie w pamięci na komputerze klienckim, aby uzyskać 2 najlepsze rekordy. Tak więc więcej danych (w tym przypadku 3 dodatkowe rekordy) zostało niepotrzebnie przesłanych przewodowo.

RBT
źródło
7

Oto, co napisałem w podobnym poście (na ten temat). (I nie, zwykle nie cytuję siebie, ale są to bardzo dobre artykuły).

„Ten artykuł jest pomocny: IQueryable vs IEnumerable w LINQ-to-SQL .

Cytując ten artykuł: „Zgodnie z dokumentacją MSDN wywołania w IQueryable działają w oparciu o wewnętrzne drzewo wyrażeń. „Te metody, które rozszerzają IQueryable (Of T), nie wykonują żadnych zapytań bezpośrednio. Zamiast tego ich funkcją jest zbudowanie obiektu Expression, który jest drzewem wyrażeń reprezentującym zapytanie skumulowane.”

Drzewa wyrażeń są bardzo ważną konstrukcją w języku C # i na platformie .NET. (Są one ogólnie ważne, ale C # czyni je bardzo przydatnymi.) Aby lepiej zrozumieć różnicę, zalecam przeczytanie o różnicach między wyrażeniami i wyrażeniami w oficjalnej specyfikacji C # 5.0 tutaj. W przypadku zaawansowanych koncepcji teoretycznych, które rozgałęziają się w rachunku lambda, wyrażenia umożliwiają obsługę metod jako obiektów pierwszej klasy. Różnica między IQueryable i IEnumerable skupia się wokół tego punktu. IQueryable buduje drzewa wyrażeń, podczas gdy IEnumerable nie działa, przynajmniej ogólnie dla tych z nas, którzy nie pracują w tajnych laboratoriach Microsoft.

Oto kolejny bardzo przydatny artykuł, który wyszczególnia różnice z perspektywy push and pull. (Przez „push” vs. „pull” mam na myśli kierunek przepływu danych. Reaktywne techniki programowania dla .NET i C #

Oto bardzo dobry artykuł, który szczegółowo opisuje różnice między wyrażeniem lambdas a wyrażeniem lambdas i omawia pojęcia warkocza wyrażeń bardziej szczegółowo: Ponowne odwiedzanie delegatów C #, drzew wyrażeń i wyrażeń lambda vs. wyrażeń lambda. . ”

devinbost
źródło
6

Używamy IEnumerablei IQueryablemanipulujemy danymi pobieranymi z bazy danych. IQueryabledziedziczy po IEnumerable, więc IQueryablezawiera wszystkie IEnumerablefunkcje. Główną różnicą pomiędzy IQueryablei IEnumerablejest IQueryableWykonuje kwerendy z filtrami natomiast IEnumerablewykonuje zapytanie, a następnie filtruje dane w oparciu o warunki.

Znajdź bardziej szczegółowe zróżnicowanie poniżej:

IEnumerable

  1. IEnumerableistnieje w System.Collectionsprzestrzeni nazw
  2. IEnumerable wykonaj wybrane zapytanie po stronie serwera, załaduj dane do pamięci po stronie klienta, a następnie przefiltruj dane
  3. IEnumerable jest odpowiedni do tworzenia zapytań o dane ze zbiorów w pamięci, takich jak List, Array
  4. IEnumerable jest korzystny dla zapytań LINQ to Object i LINQ to XML

IQueryable

  1. IQueryableistnieje w System.Linqprzestrzeni nazw
  2. IQueryable wykonuje „wybierz zapytanie” po stronie serwera ze wszystkimi filtrami
  3. IQueryable nadaje się do wysyłania zapytań o dane z kolekcji zewnętrznych (takich jak zdalna baza danych, usługa)
  4. IQueryable jest korzystny dla zapytań LINQ do SQL

IEnumerableJest więc ogólnie używany do radzenia sobie z kolekcją w pamięci, podczas gdy IQueryablejest ogólnie używany do manipulowania kolekcjami.

VikrantMore
źródło
2

IQueryable jest szybszy niż IEnumerable, jeśli mamy do czynienia z ogromnymi ilościami danych z bazy danych, ponieważ IQueryable pobiera tylko wymagane dane z bazy danych, gdzie jako IEnumerable pobiera wszystkie dane bez względu na konieczność z bazy danych

arjun bollam
źródło
0

ienumerable: kiedy chcemy poradzić sobie z pamięcią wewnętrzną, tzn. bez połączenia danych iqueryable: kiedy mamy do czynienia z serwerem sql, tj. z ilistem połączenia danych: operacje takie jak dodawanie obiektu, usuwanie obiektu itp

viswanath n
źródło