Zwracanie IEnumerable <T> vs. IQueryable <T>

1084

Jaka jest różnica między zwrotem IQueryable<T>a powrotem IEnumerable<T>, kiedy należy preferować jedno nad drugim?

IQueryable<Customer> custs = from c in db.Customers
where c.City == "<City>"
select c;

IEnumerable<Customer> custs = from c in db.Customers
where c.City == "<City>"
select c;
stackoverflowuser
źródło

Odpowiedzi:

1778

Tak, obie dadzą ci odroczoną egzekucję .

Różnica polega na tym, że IQueryable<T>jest to interfejs, który umożliwia działanie LINQ-to-SQL (LINQ.-to-cokolwiek naprawdę). Jeśli więc dopracujesz swoje zapytanie naIQueryable<T> , zapytanie to zostanie wykonane w bazie danych, jeśli to możliwe.

W tym IEnumerable<T>przypadku będzie to LINQ-to-object, co oznacza, że ​​wszystkie obiekty pasujące do pierwotnego zapytania będą musiały zostać załadowane do pamięci z bazy danych.

W kodzie:

IQueryable<Customer> custs = ...;
// Later on...
var goldCustomers = custs.Where(c => c.IsGold);

Ten kod wykona SQL, aby wybrać tylko złotych klientów. Z kolei następujący kod wykona oryginalne zapytanie w bazie danych, a następnie odfiltruje klientów niebędących złotymi w pamięci:

IEnumerable<Customer> custs = ...;
// Later on...
var goldCustomers = custs.Where(c => c.IsGold);

Jest to dość ważna różnica, a praca nad nią IQueryable<T>może w wielu przypadkach uchronić Cię przed zwróceniem zbyt wielu wierszy z bazy danych. Innym doskonałym przykładem robi stronicowania: Jeśli używasz Takei Skipna IQueryable, dostaniesz liczba wierszy żądać tylko; wykonanie tego IEnumerable<T>spowoduje, że wszystkie wiersze zostaną załadowane do pamięci.

driis
źródło
32
Świetne wyjaśnienie. Czy istnieją sytuacje, w których IEnumerable byłby lepszy niż IQueryable?
fjxx
8
Możemy więc powiedzieć, że jeśli używamy IQueryable do zapytania o obiekt pamięci, to nie będzie żadnej różnicy między IEnumerable i IQueryable?
Tarik
11
OSTRZEŻENIE: Chociaż IQueryable może być kuszącym rozwiązaniem ze względu na podaną optymalizację, nie powinno się go przepuszczać poza warstwę repozytorium lub usługi. Ma to na celu ochronę bazy danych przed narzutem spowodowanym „stosowaniem wyrażeń LINQ”.
Yorro,
48
@fjxx Tak. Jeśli chcesz powtórzyć filtrowanie oryginalnego wyniku (kilka wyników końcowych). Robi to na IQueryable interfejs sprawi kilka roundtrips do bazy danych, gdzie jak robi to na IEnumerable zrobi to filtrowanie w pamięci, dzięki czemu szybciej (chyba, że ilość danych jest ogromny)
Per Hornshøj-Schierbeck
34
Innym powodem, dla którego warto to IEnumerablezrobić, IQueryablejest to, że nie wszystkie operacje LINQ są obsługiwane przez wszystkich dostawców LINQ. Tak długo, jak wiesz, co robisz, możesz użyć, IQueryableaby przekazać jak najwięcej zapytania dostawcy LINQ (LINQ2SQL, EF, NHibernate, MongoDB itp.). Ale jeśli pozwolisz, aby inny kod robił z IQueryabletobą co tylko zechce , w końcu wpadniesz w kłopoty, ponieważ jakiś kod klienta użył nieobsługiwanej operacji. Zgadzam się z zaleceniem, aby nie wypuszczać IQueryables „na wolności” poza repozytorium lub równoważną warstwę.
Avish
302

Najlepsza odpowiedź jest dobra, ale nie wspomina o drzewach wyrażeń, które wyjaśniają „czym” różnią się oba interfejsy. Zasadniczo istnieją dwa identyczne zestawy rozszerzeń LINQ. Where(), Sum(), Count(), FirstOrDefault(), Etc wszyscy mają dwie wersje: jedną, która akceptuje funkcje i taki, który akceptuje wyrażeń.

  • IEnumerablePodpis wersja jest:Where(Func<Customer, bool> predicate)

  • IQueryablePodpis wersja jest:Where(Expression<Func<Customer, bool>> predicate)

Prawdopodobnie korzystałeś z obu, nie zdając sobie z tego sprawy, ponieważ oba są wywoływane przy użyciu identycznej składni:

np. Where(x => x.City == "<City>")działa zarówno na, jak IEnumerablei naIQueryable

  • Podczas korzystania Where()z IEnumerablekolekcji kompilator przekazuje skompilowaną funkcję doWhere()

  • Podczas korzystania Where()z IQueryablekolekcji kompilator przekazuje drzewo wyrażeń do Where(). Drzewo wyrażeń jest jak system odbicia, ale dla kodu. Kompilator przekształca kod w strukturę danych, która opisuje, co robi Twój kod, w formacie łatwym do strawienia.

Po co zawracać sobie głowę tym drzewkiem wyrażeń? Chcę tylko Where()filtrować moje dane. Głównym powodem jest to, że zarówno EF, jak i Linq2SQL ORM mogą konwertować drzewa wyrażeń bezpośrednio na SQL, gdzie kod będzie wykonywany znacznie szybciej.

Och, to brzmi jak darmowy wzrost wydajności, czy powinienem AsQueryable()w tym przypadku używać wszędzie? Nie, IQueryablejest użyteczny tylko wtedy, gdy bazowy dostawca danych może coś z tym zrobić. Konwersja czegoś takiego jak zwykły Listna IQueryablenie przyniesie żadnych korzyści.

Jakub
źródło
9
IMO jest lepsze niż zaakceptowana odpowiedź. Nie dostaję jednak jednej rzeczy: IQueryable nie daje żadnych korzyści dla zwykłych obiektów, OK, ale czy jest gorzej? Ponieważ jeśli po prostu nie daje żadnych korzyści, nie jest wystarczającym powodem, aby preferować IEnumerable, więc pomysł używania IQueryable w całym miejscu pozostaje aktualny.
Siergiej Tachenov,
1
Siergiej, IQueryable rozszerza IEnumerable, więc używając IQueryable, ładujesz do pamięci więcej niż tworzenie instancji IEnumerable! Oto argument. ( stackoverflow.com/questions/12064828/… c ++, choć myślałem, że mogę to ekstrapolować)
Viking
Uzgodnienie z Siergiejem, że jest to najlepsza odpowiedź (choć zaakceptowana odpowiedź jest w porządku). Chciałbym dodać, że w moim doświadczeniu, IQueryablenie analizowania funkcje jak IEnumerablerobi: na przykład, jeśli chcesz wiedzieć, które elementy DBSet<BookEntity>nie są w List<BookObject>, dbSetObject.Where(e => !listObject.Any(o => o.bookEntitySource == e))zgłasza wyjątek: Expression of type 'BookEntity' cannot be used for parameter of type 'BookObject' of method 'Boolean Contains[BookObject] (IEnumerable[BookObject], BookObject)'. Musiałem dodać .ToList()później dbSetObject.
Jean-David Lanz
80

Tak, oba używają odroczonego wykonania. Zilustrujmy różnicę za pomocą profilera SQL Server ....

Kiedy uruchamiamy następujący kod:

MarketDevEntities db = new MarketDevEntities();

IEnumerable<WebLog> first = db.WebLogs;
var second = first.Where(c => c.DurationSeconds > 10);
var third = second.Where(c => c.WebLogID > 100);
var result = third.Where(c => c.EmailAddress.Length > 11);

Console.Write(result.First().UserName);

W SQL Server profiler znajdujemy polecenie równe:

"SELECT * FROM [dbo].[WebLog]"

Uruchomienie tego bloku kodu w tabeli WebLog, która zawiera 1 milion rekordów, zajmuje około 90 sekund.

Tak więc wszystkie rekordy tabeli są ładowane do pamięci jako obiekty, a następnie z każdym .Where () będzie to kolejny filtr w pamięci przeciwko tym obiektom.

Kiedy używamy IQueryablezamiast IEnumerablew powyższym przykładzie (druga linia):

W SQL Server profiler znajdujemy polecenie równe:

"SELECT TOP 1 * FROM [dbo].[WebLog] WHERE [DurationSeconds] > 10 AND [WebLogID] > 100 AND LEN([EmailAddress]) > 11"

Uruchomienie tego bloku kodu za pomocą zajmuje około czterech sekund IQueryable.

IQueryable ma właściwość o nazwie, Expressionktóra przechowuje wyrażenie drzewa, które zaczyna się tworzyć, gdy użyliśmy resultw naszym przykładzie (które nazywa się odroczeniem wykonania), a na końcu to wyrażenie zostanie przekonwertowane na zapytanie SQL, które zostanie uruchomione w silniku bazy danych.

Kasper Roma
źródło
5
To uczy mnie podczas rzutowania na IEnumerable, podstawowa IQueryable traci swoją metodę rozszerzenia IQueryable.
Yiping
56

Obie dadzą ci odroczoną egzekucję, tak.

Jeśli chodzi o to, co jest preferowane w stosunku do drugiego, zależy to od tego, jakie jest twoje źródło danych.

Zwrócenie an IEnumerableautomatycznie zmusi środowisko wykonawcze do użycia LINQ do Objects w celu wykonania zapytania do kolekcji.

Zwracanie an IQueryable(który IEnumerable, nawiasem mówiąc, implementuje ) zapewnia dodatkową funkcjonalność, aby przetłumaczyć zapytanie na coś, co może lepiej działać na źródłowym źródle (LINQ na SQL, LINQ na XML itp.).

Justin Niessner
źródło
30

Ogólnie rzecz biorąc, zaleciłbym następujące:

  • Zwróć, IQueryable<T>jeśli chcesz włączyć programistę przy użyciu tej metody, aby zawęzić zapytanie zwracane przed wykonaniem.

  • Zwróć, IEnumerablejeśli chcesz przetransportować zestaw obiektów do wyliczenia.

Wyobraź sobie, IQueryableże to, co to jest - „zapytanie” o dane (które możesz zawęzić, jeśli chcesz). An IEnumerableto zbiór obiektów (które już zostały otrzymane lub zostały utworzone), nad którymi można wyliczyć.

sebastianmehler
źródło
2
„Canumerate”, „not” can IEnumerable. ”
Casey
28

Wiele już powiedziano, ale wracając do korzeni, w bardziej techniczny sposób:

  1. 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ą.
  2. 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.

Arman McHitarian
źródło
3
Komentarz, że elementy IEnumerable są zawsze w pamięci, niekoniecznie musi być prawdziwy. Interfejs IQueryable implementuje interfejs IEnumerable. Z tego powodu możesz przekazać surową IQueryable, która reprezentuje zapytanie LINQ-SQL bezpośrednio w widoku, który oczekuje IEnumerable! Możesz być zaskoczony, gdy kontekst danych wygasł lub że masz problemy z MARS (wiele aktywnych zestawów wyników).
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. Ale naprawdę rozumiem twój punkt widzenia. Dodam komentarz na ten temat.
Arman McHitarian
@AlexanderPritchard żaden z nich nie jest obiektem w pamięci, ponieważ sam w sobie nie jest prawdziwym typem, jest markerem pewnego rodzaju - jeśli chcesz wejść 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.
Arman McHitarian
24

Ogólnie rzecz biorąc, chcesz zachować oryginalny typ statyczny zapytania, dopóki nie będzie to miało znaczenia.

Z tego powodu możesz zdefiniować zmienną jako „var” zamiast jednego IQueryable<>lub, IEnumerable<>a będziesz wiedział, że nie zmieniasz typu.

Jeśli zaczynasz od IQueryable<>, zazwyczaj chcesz go zachować, IQueryable<>dopóki nie będzie istotnych powodów, aby go zmienić. Powodem tego jest to, że chcesz podać procesorowi zapytań jak najwięcej informacji. Na przykład, jeśli zamierzasz użyć tylko 10 wyników (zadzwoniłeś Take(10)), chcesz, aby SQL Server wiedział o tym, aby mógł zoptymalizować swoje plany zapytań i wysłać ci tylko te dane, których użyjesz.

Istotnym powodem do zmiany typu z IQueryable<>na IEnumerable<>może być to, że wywołujesz funkcję rozszerzenia, której implementacja IQueryable<>w twoim obiekcie nie może obsłużyć lub nieefektywnie. W takim przypadku możesz przekonwertować typ na IEnumerable<>(przypisując do zmiennej typu)IEnumerable<> lub AsEnumerablena przykład przy użyciu metody rozszerzenia), aby wywoływane funkcje rozszerzeń były tymi, które są w Enumerableklasie zamiast w Queryableklasie.

AJS
źródło
18

Istnieje post na blogu z krótkim przykładem kodu źródłowego na temat tego, jak niewłaściwe użycie IEnumerable<T>może dramatycznie wpłynąć na wydajność zapytania LINQ: Entity Framework: IQueryable vs. IEnumerable .

Jeśli zagłębimy się głębiej i zajrzymy do źródeł, zobaczymy, że istnieją oczywiście różne metody rozszerzenia IEnumerable<T>:

// Type: System.Linq.Enumerable
// Assembly: System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
// Assembly location: C:\Windows\Microsoft.NET\Framework\v4.0.30319\System.Core.dll
public static class Enumerable
{
    public static IEnumerable<TSource> Where<TSource>(
        this IEnumerable<TSource> source, 
        Func<TSource, bool> predicate)
    {
        return (IEnumerable<TSource>) 
            new Enumerable.WhereEnumerableIterator<TSource>(source, predicate);
    }
}

i IQueryable<T>:

// Type: System.Linq.Queryable
// Assembly: System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
// Assembly location: C:\Windows\Microsoft.NET\Framework\v4.0.30319\System.Core.dll
public static class Queryable
{
    public static IQueryable<TSource> Where<TSource>(
        this IQueryable<TSource> source, 
        Expression<Func<TSource, bool>> predicate)
    {
        return source.Provider.CreateQuery<TSource>(
            Expression.Call(
                null, 
                ((MethodInfo) MethodBase.GetCurrentMethod()).MakeGenericMethod(
                    new Type[] { typeof(TSource) }), 
                    new Expression[] 
                        { source.Expression, Expression.Quote(predicate) }));
    }
}

Pierwszy zwraca iterator zliczalny, a drugi tworzy zapytanie za pośrednictwem dostawcy zapytań określonego w IQueryableźródle.

Oleksander Iwanicki
źródło
11

Niedawno napotkałem problem z IEnumerablev IQueryable. Algorytm użyty jako pierwszy wykonał IQueryablezapytanie w celu uzyskania zestawu wyników. Zostały one następnie przekazane do foreachpętli, z elementami utworzonymi jako klasa Entity Framework (EF). Ta klasa EF została następnie użyta w fromklauzuli zapytania Linq to Entity, co spowodowało, że wynik byłIEnumerable .

Jestem dość nowy w EF i Linq dla Entities, więc zajęło mi trochę czasu, aby dowiedzieć się, jakie było wąskie gardło. Korzystając z MiniProfiling, znalazłem zapytanie, a następnie przekonwertowałem wszystkie poszczególne operacje na pojedyncze IQueryablezapytanie Linq for Entities. Wykonanie IEnumerablezajęło 15 sekund, a wykonanie IQueryablezajęło 0,5 sekundy. W grę wchodziły trzy tabele i po przeczytaniu tego uważam, że IEnumerablezapytanie faktycznie tworzyło trzy tabele dla wielu produktów i filtrowało wyniki.

Spróbuj użyć IQueryables jako ogólnej zasady i profiluj swoją pracę, aby zmiany były mierzalne.

sscheider
źródło
powodem było to, że wyrażenia IQueryable są konwertowane na natywny SQL w EF i wykonywane bezpośrednio w DB, podczas gdy listy IEnumerable są obiektami w pamięci. Są pobierane z DB w pewnym momencie, gdy wywołujesz funkcje agregujące, takie jak Count, Sum lub dowolne To ..., a następnie operujesz w pamięci. IQueryables również utknęły w pamięci po wywołaniu jednego z tych interfejsów API, ale jeśli nie, możesz przekazać wyrażenie w górę stosu warstw i bawić się filtrami aż do wywołania interfejsu API. Dobrze zaprojektowany DAL jako dobrze zaprojektowane repozytorium rozwiąże tego rodzaju problemy;)
Arman McHitarian
10

Chciałbym wyjaśnić kilka rzeczy ze względu na pozornie sprzeczne odpowiedzi (głównie otaczające IEnumerable).

(1) IQueryablerozszerza IEnumerableinterfejs. (Możesz wysłać IQueryabledo czegoś, co oczekuje IEnumerablebezbłędnie.)

(2) Zarówno LINQ, jak IQueryablei IEnumerablepróba opóźnionego ładowania podczas iteracji nad zestawem wyników. (Należy zauważyć, że implementację można zobaczyć w metodach rozszerzenia interfejsu dla każdego typu).

Innymi słowy, IEnumerablesnie są wyłącznie „w pamięci”. IQueryablesnie zawsze są wykonywane w bazie danych. IEnumerablemusi załadować rzeczy do pamięci (raz pobrane, być może leniwie), ponieważ nie ma abstrakcyjnego dostawcy danych. IQueryablespolegać na abstrakcyjnym dostawcy (takim jak LINQ-to-SQL), chociaż może to być również dostawca in-memory .NET.

Przykładowy przypadek użycia

(a) Pobierz listę rekordów IQueryablez kontekstu EF. (Brak zapisów w pamięci.)

(b) Przekaż IQueryabledo widoku, którego modelem jest IEnumerable. (Ważny. IQueryableRozszerza IEnumerable.)

(c) Iteruj i uzyskuj dostęp do rekordów, encji potomnych i właściwości zestawu danych z widoku. (Może powodować wyjątki!)

Możliwe problemy

(1) IEnumerablePróby opóźnionego ładowania i kontekst danych wygasły. Zgłoszono wyjątek, ponieważ dostawca nie jest już dostępny.

(2) Serwery proxy encji Entity Framework są włączone (domyślne), a użytkownik próbuje uzyskać dostęp do pokrewnego (wirtualnego) obiektu z wygasłym kontekstem danych. Taki sam jak (1).

(3) Wiele aktywnych zestawów wyników (MARS). Jeśli iterujesz IEnumerablew foreach( var record in resultSet )bloku i jednocześnie record.childEntity.childPropertypróbujesz uzyskać dostęp , możesz skończyć z MARS z powodu leniwego ładowania zarówno zestawu danych, jak i jednostki relacyjnej. Spowoduje to wyjątek, jeśli nie zostanie włączony w ciągu połączenia.

Rozwiązanie

  • Odkryłem, że włączenie MARS w ciągu połączenia działa niewiarygodnie. Sugeruję unikanie MARS, chyba że jest to dobrze zrozumiane i wyraźnie pożądane.

Wykonaj zapytanie i zapisz wyniki, wywołując resultList = resultSet.ToList() To wydaje się być najprostszym sposobem na zapewnienie, że twoje byty są w pamięci.

W przypadkach, w których uzyskujesz dostęp do powiązanych podmiotów, nadal możesz wymagać kontekstu danych. Albo to, albo możesz wyłączyć serwery proxy i Includepodmioty wyraźnie powiązane z twoim DbSet.


źródło
9

Główna różnica między „IEnumerable” i „IQueryable” polega na tym, gdzie wykonywana jest logika filtru. Jeden wykonuje po stronie klienta (w pamięci), a drugi w bazie danych.

Na przykład możemy rozważyć przykład, w którym mamy 10 000 rekordów dla użytkownika w naszej bazie danych i powiedzmy, że tylko 900 spośród nich jest aktywnymi użytkownikami, więc w tym przypadku, jeśli użyjemy „IEnumerable”, najpierw ładuje wszystkie 10 000 rekordów do pamięci i następnie stosuje na nim filtr IsActive, który ostatecznie zwraca 900 aktywnych użytkowników.

Z drugiej strony w tym samym przypadku, jeśli użyjemy „IQueryable”, zastosuje on bezpośrednio filtr IsActive w bazie danych, który bezpośrednio stamtąd zwróci 900 aktywnych użytkowników.

Link referencyjny

Tabish Usman
źródło
który jest zoptymalizowany i lekki pod względem wydajności?
Sitecore Sam
@Sam „IQueryable” jest bardziej preferowany pod względem optymalizacji i lekkości.
Tabish Usman
6

Możemy używać obu w ten sam sposób, a różnią się one jedynie wydajnością.

IQueryable wykonuje się tylko względem bazy danych w efektywny sposób. Oznacza to, że tworzy całe wybrane zapytanie i pobiera tylko powiązane rekordy.

Na przykład chcemy wziąć 10 najlepszych klientów, których nazwa zaczyna się od „Nimal”. W takim przypadku wybrane zapytanie zostanie wygenerowane jako select top 10 * from Customer where name like ‘Nimal%’.

Ale jeśli użyjemy IEnumerable, zapytanie będzie podobne, select * from Customer where name like ‘Nimal%’a pierwsza dziesiątka zostanie przefiltrowana na poziomie kodowania C # (pobiera wszystkie rekordy klientów z bazy danych i przekazuje je do C #).

użytkownik3710357
źródło
5

Oprócz pierwszych 2 naprawdę dobrych odpowiedzi (autorstwa driis i Jacoba):

Interfejs IEnumerable znajduje się w przestrzeni nazw System.Collections.

Obiekt IEnumerable reprezentuje zestaw danych w pamięci i może poruszać się po tych danych tylko do przodu. Zapytanie reprezentowane przez obiekt IEnumerable jest wykonywane natychmiastowo i całkowicie, dzięki czemu aplikacja szybko otrzymuje dane.

Kiedy zapytanie jest wykonywane, IEnumerable ładuje wszystkie dane, a jeśli musimy je filtrować, samo filtrowanie odbywa się po stronie klienta.

Interfejs IQueryable znajduje się w przestrzeni nazw System.Linq.

Obiekt IQueryable zapewnia zdalny dostęp do bazy danych i umożliwia nawigację po danych w bezpośredniej kolejności od początku do końca lub w odwrotnej kolejności. Podczas tworzenia zapytania zwracany obiekt jest IQueryable, zapytanie jest optymalizowane. W rezultacie mniej pamięci jest zużywane podczas jego wykonywania, mniejsza przepustowość sieci, ale jednocześnie można ją przetwarzać nieco wolniej niż zapytanie zwracające obiekt IEnumerable.

Co wybrać

Jeśli potrzebujesz całego zestawu zwracanych danych, lepiej użyć IEnumerable, który zapewnia maksymalną prędkość.

Jeśli NIE potrzebujesz całego zestawu zwracanych danych, a tylko niektóre odfiltrowane dane, lepiej użyć IQueryable.

Gleb B
źródło
0

Oprócz powyższego warto zauważyć, że możesz uzyskać wyjątki, jeśli użyjesz IQueryablezamiast IEnumerable:

Następujące działa dobrze, jeśli productsjest IEnumerable:

products.Skip(-4);

Jeśli jednak productsjest IQueryablei próbuje uzyskać dostęp do rekordów z tabeli DB, otrzymasz następujący błąd:

Przesunięcie określone w klauzuli OFFSET nie może być ujemne.

Jest tak, ponieważ zbudowano następujące zapytanie:

SELECT [p].[ProductId]
FROM [Products] AS [p]
ORDER BY (SELECT 1)
OFFSET @__p_0 ROWS

a PRZESUNIĘCIE nie może mieć wartości ujemnej.

David Klempfner
źródło