2010-07-22 10 views
42

Biorąc pod uwagę następujące LINQ do kwerendy SQL:Zrozumienie .AsEnumerable() w LINQ to SQL

var test = from i in Imports 
      where i.IsActive 
      select i; 

Interpretowany SQL jest:

SELECT [t0].[id] AS [Id] .... FROM [Imports] AS [t0] WHERE [t0].[isActive] = 1 

Say chciałem wykonać pewne działania w select których nie można przekonwertować na SQL. Rozumiem, że konwencjonalnym sposobem osiągnięcia tego jest wykonanie go w ten sposób, aby przekształcić go w działający obiekt.

Biorąc pod uwagę ten zaktualizowany kod:

var test = from i in Imports.AsEnumerable() 
      where i.IsActive 
      select new 
      { 
       // Make some method call 
      }; 

i aktualizowane SQL:

SELECT [t0].[id] AS [Id] ... FROM [Imports] AS [t0] 

zauważyć brak WHERE w wykonywanej instrukcji SQL.

Czy to oznacza, że ​​cała tabela "Import" jest zapisana w pamięci podręcznej? Czy ta powolna wydajność w ogóle, gdyby tabela zawierała dużą liczbę rekordów?

Pomóż mi zrozumieć, co dzieje się za kulisami.

+1

Doskonałe pytanie! – fernandoespinosa

+0

Wystarczy popatrzeć na przykłady z innym pytaniem: [wpisać opis link tutaj] [1] [1]: http://stackoverflow.com/a/14129116/1792434 – lukaszk

Odpowiedz

33

Powodem AsEnumerable jest

AsEnumerable (TSource) (IEnumerable (TSource)) mogą być wykorzystane do wyboru zapytania implementacje gdy sekwencja realizuje IEnumerable (T), ale także się inny zestaw dostępnych publicznie zapytań dostępnych metod

Kiedy wcześniej dzwoniłeś do metody Where, dzwoniłeś do innej metody Where z IEnume rable.Where. Tam, gdzie instrukcja była dla LINQ, aby konwertować do SQL, nowa Gdzie jest IEnumerable, która pobiera IEnumerable, wylicza ją i daje pasujące elementy. Co wyjaśnia, dlaczego widzisz różne generowane SQL. Tabela zostanie pobrana w całości z bazy danych przed zastosowaniem rozszerzenia Gdzie w drugiej wersji kodu. Może to stworzyć poważny szyjkę butelki, ponieważ cały stół musi znajdować się w pamięci lub, co gorsza, cały stół musiałby przechodzić między serwerami. Zezwól serwerowi SQL na wykonanie Where i rób to, co robi najlepiej.

+1

więc nadal Czy odroczono wykonanie poprawnie? Używając powyższego zapytania jako przykładu, cała tabela "Importów" nie będzie przechowywana w pamięci tylko tych aktywnych, kiedy zrobię coś w stylu "test.Dump()". Czy to prawda? –

+0

@Mike Fielden Cóż, cały stół przejdzie przez pamięć w miejscu, gdzie jest wyliczana. Nie jestem pewien, czy wszystkie wiersze zostaną załadowane do pamięci w tym samym czasie. –

+0

@YuriyFaktorovich: Dlaczego więc istnieje? Uprzejmie proszę także o tym, żeby prosili o używanie AsEnumerable. Dzięki – Unbreakable

1

Wierzę, że AsEnumerable po prostu mówi kompilatorowi, jakie metody rozszerzeń użyć (w tym przypadku te zdefiniowane dla IEnumerable zamiast tych dla IQueryable). Wykonanie zapytania jest nadal odłożone, dopóki nie wywołasz ToArray lub wyliczyć na nim.

+0

To nie ma żadnego sensu. AsEnumerable to metoda. Nie "mówi kompilatorowi" niczego w szczególności. –

+2

Tak, jest to metoda, ale jej typem zwrotnym jest Enumerable, dlatego kompilator użyje różnych metod podczas kompilowania zapytania LINQ (te, które są liczone). Z dokumentacji: Metoda AsEnumerable (IEnumerable ) nie ma innego efektu niż zmiana typu źródła czasu kompilacji z typu, który implementuje IEnumerable do samego IEnumerable . http://msdn.microsoft.com/en-us/library/bb335435.aspx – Razvi

6

W miejscu, w którym wyliczenie jest wyliczane, baza danych zostanie następnie przeszukiwana, a cały zestaw wyników zostanie pobrany.

Rozwiązaniem może być rozwiązanie częściowe i częściowe. Rozważmy

var res = (
    from result in SomeSource 
    where DatabaseConvertableCriterion(result) 
    && NonDatabaseConvertableCriterion(result) 
    select new {result.A, result.B} 
); 

Powiedzmy, że NonDatabaseConvertableCriterion wymaga pola C od wyniku. Ponieważ NonDatabaseConvertableCriterion robi to, co sugeruje jego nazwa, musi to być wykonane jako wyliczenie.Jednak należy wziąć pod uwagę:

var partWay = 
(
    from result in SomeSource 
    where DatabaseConvertableCriterion(result) 
    select new {result.A, result.B, result.C} 
); 
var res = 
(
    from result in partWay.AsEnumerable() 
    where NonDatabaseConvertableCriterion select new {result.A, result.B} 
); 

w tym przypadku, gdy res jest wyliczone, zapytaliśmy lub wykorzystywane w inny sposób, jak wiele pracy, jak to możliwe będą przekazywane do bazy danych, która powróci na tyle, aby kontynuować pracę. Zakładając, że rzeczywiście niemożliwe jest przepisanie, aby cała praca mogła zostać wysłana do bazy danych, może to być odpowiedni kompromis.

4

Istnieją trzy implementacje AsEnumerable.

DataTableExtensions.AsEnumerable

rozszerza DataTable dać mu interfejs IEnumerable więc można używać LINQ przeciwko DataTable.

Enumerable.AsEnumerable<TSource> i ParallelEnumerable.AsEnumerable<TSource>

Sposób AsEnumerable<TSource>(IEnumerable<TSource>) ma wpływ inne niż zmienić typ kompilacji źródła od typu, realizuje IEnumerable<T> do IEnumerable<T> siebie.

AsEnumerable<TSource>(IEnumerable<TSource>) mogą być wykorzystane do wyboru pomiędzy implementacjami zapytań gdy sekwencja realizuje IEnumerable<T> ale ma również inny zestaw metod zapytań publicznych dostępnych. Na przykład, biorąc pod uwagę ogólny klasę Table który implementuje IEnumerable<T> i ma swoje własne metody, takie jak Where, Select, a SelectMany, wywołanie Where byłoby powołać się na publiczną Where metodę Table. Typ Table reprezentujący tabelę bazy danych może mieć metodę Where, która pobiera argument predykatów jako drzewo wyrażeń i konwertuje drzewo do SQL w celu zdalnego wykonania. Jeśli zdalne wykonanie nie jest pożądane, na przykład dlatego, że predykat wywołuje lokalną metodę , metoda może być użyta do ukrycia niestandardowych metod , a zamiast tego udostępnić standardowe operatory zapytań .

Innymi słowy.

Jeśli mam

IQueryable<X> sequence = ...; 

od A LinqProvider, jak Entity Framework, i ja,

sequence.Where(x => SomeUnusualPredicate(x)); 

że zapytanie zostanie skomponowane i uruchamiane na serwerze. To się nie powiedzie w czasie wykonywania, ponieważ EntityFramework nie wie, jak przekonwertować SomeUnusualPredicate na SQL.

Jeśli chcesz, aby uruchomić instrukcję z LINQ to Objects zamiast tego zrobić,

sequence.AsEnumerable().Where(x => SomeUnusualPredicate(x)); 

teraz serwer zwróci wszystkie dane i Enumerable.Where z Linq do obiektów będzie używany zamiast Query Implementacja dostawcy.

Nie ma znaczenia, że ​​Entity Framework nie wie, jak interpretować SomeUnusualPredicate, moja funkcja będzie używana bezpośrednio. (Jednak może to być nieefektywne podejście, ponieważ wszystkie wiersze zostaną zwrócone z serwera.)