Różnice między IQueryable, List, IEnumerator?

Zastanawiam się jaka jest różnica między IQueryable, List, IEnumerator i kiedy powinienem użyć każdego z nich?

Na przykład przy użyciu Linq do SQL zrobiłbym coś takiego:

public List<User> GetUsers()
{
   return db.User.where(/* some query here */).ToList();
}

Teraz zastanawiam się, czy zamiast tego powinienem używać IQueryable. Nie jestem pewien zalet używania go nad listą.

Author: Community, 2011-01-30

3 answers

IQueryable<T> ma na celu umożliwienie dostawcy zapytań (na przykład ORM, taki jak LINQ to SQL lub Encja Framework) użycia wyrażeń zawartych w zapytaniu w celu przetłumaczenia żądania na inny format. Innymi słowy, LINQ-to-SQL patrzy na właściwości encji, których używasz wraz z porównaniami, które robisz, i faktycznie tworzy instrukcję SQL w celu wyrażenia (miejmy nadzieję) równoważnego żądania.

IEnumerable<T> jest bardziej ogólny niż IQueryable<T> (chociaż wszystkie instancje IQueryable<T> implementacja IEnumerable<T>) i definiuje tylko sekwencję. Jednak istnieją metody rozszerzenia dostępne w klasie Enumerable, które definiują pewne operatory typu zapytania w tym interfejsie i używają zwykłego kodu do oceny tych warunków.

List<T> jest tylko formatem wyjściowym i chociaż implementuje IEnumerable<T>, nie jest bezpośrednio związany z zapytaniem.

Innymi słowy, kiedy używasz IQueryable<T>, definiujesz wyrażenie , które jest tłumaczone na coś innego. Nawet jeśli jesteś pisanie kodu, ten kod nigdy nie zostanie wykonany , tylko zostanie sprawdzony i zamieniony w coś innego, jak rzeczywiste zapytanie SQL. Z tego powodu tylko pewne rzeczy są ważne w tych wyrażeniach. Na przykład, nie możesz wywołać zwykłej funkcji, którą definiujesz z poziomu tych wyrażeń, ponieważ LINQ-to-SQL nie wie, jak zmienić wywołanie w instrukcję SQL. Niestety większość z tych ograniczeń jest oceniana tylko podczas wykonywania.

Kiedy używasz IEnumerable<T> do zapytań używasz LINQ-to-Objects, co oznacza, że piszesz rzeczywisty kod, który jest używany do oceny zapytania lub przekształcania wyników, więc generalnie nie ma ograniczeń co do tego, co możesz zrobić. Z tych wyrażeń można dowolnie wywoływać inne funkcje.

Z LINQ do SQL

Idąc w parze z powyższym rozróżnieniem, ważne jest również, aby pamiętać, jak to działa w praktyce. Gdy piszesz Zapytanie o dane Klasa kontekstowa w LINQ do SQL, tworzy IQueryable<T>. Cokolwiek zrobisz przeciwko samej IQueryable<T> zostanie zamienione w SQL, więc filtrowanie i transformacja zostaną wykonane na serwerze. Cokolwiek zrobisz przeciwko temu jako IEnumerable<T>, zostaną wykonane na poziomie aplikacji. Czasami jest to pożądane (na przykład w przypadku, gdy musisz użyć kodu po stronie klienta), ale w wielu przypadkach jest to niezamierzone.

Na przykład, gdybym miał kontekst z Customers właściwość reprezentująca Customer tabelę, a każdy klient ma CustomerId kolumnę, spójrzmy na dwa sposoby wykonania tego zapytania:

var query = (from c in db.Customers where c.CustomerId == 5 select c).First();

Spowoduje to wygenerowanie SQL, który zapyta bazę danych o rekord Customer o CustomerId równy 5. Coś w stylu:

select CustomerId, FirstName, LastName from Customer where CustomerId = 5

Co się stanie, jeśli zamienimy Customers w IEnumerable<Customer> używając metody rozszerzenia AsEnumerable()?

var query = (from c in db.Customers.AsEnumerable() where c.CustomerId == 5 select c).First();

Ta prosta zmiana ma poważne konsekwencje. Ponieważ zamieniamy {[21] } w IEnumerable<Customer>, to przyniesie całą zwróć tabelę i przefiltruj ją po stronie klienta(cóż, ściśle mówiąc, spowoduje to zwrócenie każdego wiersza w tabeli , dopóki nie napotka takiego, który pasuje do kryteriów , ale punkt jest taki sam).

ToList()

Do tej pory rozmawialiśmy tylko o IQueryable i IEnumerable. To dlatego, że są one podobne, komplementarne interfejsy. W obu przypadkach definiujesz zapytanie; to znaczy definiujesz gdzie , aby znaleźć dane, co filtry do zastosowania i jakie dane zwrócić. Obie z nich są zapytaniami
query = from c in db.Customers where c.CustomerId == 5 select c;
query = from c in db.Customers.AsEnumerable() where c.CustomerId == 5 select c;

Jak już mówiliśmy, pierwsze zapytanie używa IQueryable, a drugie używa IEnumerable. W obu przypadkach jest to jednak tylko zapytanie. Zdefiniowanie zapytania w rzeczywistości nie robi nic przeciwko źródłu danych. Zapytanie jest faktycznie wykonywane, gdy kod zaczyna iterować nad listą. Może się to zdarzyć na wiele sposobów; pętla foreach, wywołanie ToList(), itd.

Zapytanie zostanie wykonane pierwszy i co czas jest iteracyjny. Jeśli zadzwonisz ToList() na query dwa razy, skończysz z dwiema listami z zupełnie odrębnymi obiektami. Mogą zawierać te same dane, ale będą to różne odniesienia.

Edytuj po komentarzach

Chcę tylko jasno określić różnicę między tym, kiedy rzeczy są robione po stronie klienta i kiedy są robione po stronie serwera. Jeśli odwołujesz się do IQueryable<T> jako IEnumerable<T>, only the querying done after it ' s an IEnumerable<T> will be done client-side. Na przykład, powiedzmy, że mam tę tabelę i kontekst LINQ-to-SQL:

Customer
-----------
CustomerId
FirstName
LastName

Najpierw konstruuję zapytanie na podstawie FirstName. To tworzy IQueryable<Customer>:

var query = from c in db.Customers where c.FirstName.StartsWith("Ad") select c;

Teraz przekazuję to zapytanie do funkcji, która bierze IEnumerable<Customer> i robi pewne filtrowanie na podstawie LastName:

public void DoStuff(IEnumerable<Customer> customers)
{
    foreach(var cust in from c in customers where c.LastName.StartsWith("Ro"))
    {
        Console.WriteLine(cust.CustomerId);
    }
}

Zrobiliśmy drugie zapytanie tutaj, ale jest robione na IEnumerable<Customer>. To, co się tutaj stanie, to to, że pierwsze zapytanie zostanie ocenione, uruchomione ten SQL:

select CustomerId, FirstName, LastName from Customer where FirstName like 'Ad%'

Więc sprowadzimy z powrotem wszystkich, których FirstName zaczyna się od "Ad". Zauważ, że nie ma tu nic o LastName. To dlatego, że jest filtrowany po stronie klienta.

Po przywróceniu tych wyników, program będzie następnie powtarzał wyniki i dostarczał tylko rekordy, których LastName zaczyna się od "Ro". Minusem tego jest to, że przywróciliśmy dane-mianowicie wszystkie wiersze, których LastName Nie zaczyna się od "Ro"--że może zostały odfiltrowane na serwerze.

 125
Author: Adam Robinson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-08-06 15:37:08

IQueryable<T>: abstrakcyjny dostęp do bazy danych, wspiera leniwą ocenę zapytań
List<T>: zbiór wpisów. Brak wsparcia dla leniwej oceny
IEnumerator<T>: dostarcza możliwość iteracji nad i IEnumerable<T> (które są zarówno IQueryable<T> jak i List<T>)

Problem z tym kodem jest dość prosty - zawsze wykonuje zapytanie, gdy jest wywołane. Jeśli zamiast tego zwrócisz db.User.Where(...) (co jest IQueryable<T>), zachowasz ewaluację zapytania, dopóki nie będzie ono rzeczywiście potrzebne(iterowane). Również, jeśli użytkownik tej metody musiałby określić dalsze predykaty, które również będą wykonywane w bazie danych, co znacznie przyspiesza.

 10
Author: Femaref,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-01-30 18:30:08

Użyj iList LUB List<item>, gdy chcesz mocno wpisaną kolekcję jakiegoś encji.

I używaj Iqueryable i Ienumurator Gdy chcesz uzyskać głupie dane jako zbiór obiektów, powróci jako luźna kolekcja typów i bez ograniczeń.

Wolałbym użyć List<type>, ponieważ używając zawijania listy i rzucania w zbiorze silnie wpisz mój zestaw wyników.

Ponadto, korzystanie z listy daje możliwość dodawania, sortowania i konwersji warstwy do tablicy, Ienumurator lub jako Queryable.

 1
Author: Mahmoud Sayed,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-07-06 04:00:29