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ą.
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 oIQueryable
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.
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 ocenyIEnumerator<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.
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.
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