2011-02-04 16 views
8

Pozdrowienia, mam następujące pytanie. Nie znalazłem na to dokładnej odpowiedzi i jest to dla mnie naprawdę interesujące. Załóżmy, że mam następujący kod, który pobiera rekordy z bazy danych (na przykład, aby wyeksportować je do pliku XML).Czy LINQ jest leniwy?

var result = from emps in dc.Employees 
    where emps.age > 21 
    select emps; 

foreach (var emp in result) { 
    // Append this record in suitable format to the end of XML file 
} 

Załóżmy, że istnieje milion rekordów spełniających gdzie warunek w kodzie. Co się stanie? Wszystkie te dane będą pobierane z programu SQL Server natychmiast do pamięci środowiska wykonawczego, gdy dotrą do konstruktu foreach, lub zostanie on następnie pobrany, co jest konieczne, pierwszy rekord, drugi. Innymi słowy, czy LINQ naprawdę radzi sobie z sytuacją z iterowaniem przez duże kolekcje (zobacz mój post tutaj dla szczegółów)?

Jeśli nie, jak rozwiązać problemy z pamięcią w takim przypadku? Jeśli naprawdę muszę przejść przez dużą kolekcję, co powinienem zrobić? Oblicz rzeczywistą ilość elementów w kolekcji za pomocą funkcji Count, a następnie odczytaj dane z bazy danych małymi porcjami. Czy istnieje prosty sposób na wdrożenie stronicowania przy użyciu frameworka LINQ?

Odpowiedz

8

Wszystkie dane zostaną pobrane z serwera SQL, za jednym razem, i wprowadzone do pamięci. Jedynym sposobem, jaki mogę sobie wyobrazić, jest przetwarzanie danych w mniejszych porcjach (takich jak strona za pomocą Skip() i Take()). Ale to oczywiście wymaga więcej uderzeń do SQL Server.

Oto LINQ metodę rozszerzenia stronicowania pisałem to zrobić:

public static IEnumerable<TSource> Page<TSource>(this IEnumerable<TSource> source, int pageNumber, int pageSize) 
    { 
     return source.Skip((pageNumber - 1) * pageSize).Take(pageSize); 
    } 
+0

+1 Używam do tego prawie identycznej funkcji. – alex

+0

Nie sądzę, że więcej trafień do SQL Server będzie tak wielkim problemem z wydajnością, jeśli ustawimy poprawnie rozmiar strony. Powinno to być około 1000 wierszy na moim przykładzie, a gdy aplikacja będzie głównie zajęta eksportowaniem, nie będzie wysyłania zapytań do bazy danych. –

+0

@ Spirit_1984 - Zgadzam się z tobą. Wolałbym mieć więcej haseł do SQL Server niż próba załadowania milionów wierszy do pamięci. –

4

Tak, LINQ wykorzystuje oceny leniwy. Baza danych byłaby przeszukiwana, gdy foreach zaczyna się uruchamiać, ale pobierałby wszystkie dane za jednym razem (znacznie mniej wydajne byłoby wykonywanie milionów zapytań dla tylko jednego wyniku na raz).

Jeśli obawiasz się, że za jednym razem uzyskasz zbyt wiele wyników, możesz użyć funkcji Pomiń i u góry, aby uzyskać ograniczoną liczbę wyników naraz (w ten sposób dzieląc stronę na wynik).

1

Zostanie pobrany po wywołaniu ToList lub podobnych metod. LINQ wstrzymuje wykonanie:

okazji - nawet mając wykonanie odraczane i ładuje całą kolekcję ze źródła danych w przypadku OR/M lub innego dostawcy LINQ - będzie określony przez implementatora źródła obiektu LINQ.

To na przykład niektóre OR/M mogą zapewniać leniwy załadunek, a to oznacza, że ​​"cała lista klientów" będzie czymś w rodzaju kursora i dostęp do jednego z elementów (pracownika), a także jednej nieruchomości , załaduje tylko pracownika lub dostęp do nieruchomości.

Ale, w każdym razie, to są podstawy.

EDYCJA: Teraz widzę, że jest to LINQ-SQL ... Albo nie wiem, czy autor pytania źle zrozumiał LINQ, a on nie wie, LINQ nie jest LINQ-SQL, ale jest bardziej wzór i funkcja językowa.

0

OK, teraz dzięki tej answer mam pomysł - jak o mieszanie funkcji biorąc stronę z plastyczności zamian możliwości?Oto przykładowy kod:

// This is the original function that takes the page 
public static IEnumerable<TSource> Page<TSource>(this IEnumerable<TSource> source, int pageNumber, int pageSize) { 
    return source.Skip((pageNumber - 1) * pageSize).Take(pageSize); 
} 

// And here is the function with yield implementation 
public static IEnumerable<TSource> Lazy<TSource>(this IEnumerable<TSource> source, int pageSize) { 
    int pageNumber = 1; 
    int count = 0; 

    do { 
    IEnumerable<TSource> coll = Page(source, pageNumber, pageSize); 
    count = coll.Count(); 
    pageNumber++; 
    yield return coll; 
    } while (count > 0); 
} 


// And here goes our code for traversing collection with paging and foreach 
var result = from emps in dc.Employees 
    where emps.age > 21 
    select emps; 

// Let's use the 1000 page size 
foreach (var emp in Lazy(result, 1000)) { 
    // Append this record in suitable format to the end of XML file 
} 

myślę w ten sposób możemy przezwyciężyć ten problem pamięci, a jednocześnie pozostawiając składniach z foreach nie tak skomplikowane.

Powiązane problemy