2010-09-10 7 views
10

Powtarzam przez małą tablicę (~ 10GB) z foreach/IQueryable i LINQ-SQL. Wygląda mniej więcej tak:Iterowanie poprzez IQueryable z foreach skutkuje wyjątkiem braku pamięci

using (var conn = new DbEntities() { CommandTimeout = 600*100}) 
{ 
    var dtable = conn.DailyResults.Where(dr => dr.DailyTransactionTypeID == 1); 
    foreach (var dailyResult in dtable) 
    { 
     //Math here, results stored in-memory, but this table is very small. 
     //At the very least compared to stuff I already have in memory. :) 
    } 
} 

Visual Studio debugger rzuca out-of wyjątkiem pamięci po krótkim czasie na bazie pętli foreach. Zakładam, że wiersze tabeli dtable nie są przepłukiwane. Co robić?

+0

co masz już zapisany w pamięci, który jest większy niż 10 GB? Czy chodziło Ci o 10 MB? – msarchet

+0

Mam 16 GB pamięci na tym komputerze, ale co najmniej połowa z nich jest używana przez cache Windows plus pamięć podręczną SQL. Nie mogłem zmieścić 10 GB pamięci, więc skończyło mi się to. Jestem zaskoczony, że IQueryable pobiera całą tabelę ... Spodziewam się, że pobierze jedną lub małą liczbę wierszy naraz. – Gleno

+0

Wydaje mi się, że udało mi się ominąć to nieco, zmieniając cel kompilacji na x64 zamiast na x86, który wykorzystuje więcej pamięci na moim komputerze. Jednak dane, które przetwarzam w pętli foreach, nie są ogromne, więc myślę, że rzeczy wewnątrz pętli nie zbierają poprawnie śmieci. –

Odpowiedz

12

IQueryable<DailyResult> dtable będzie próbował załadować cały wynik zapytania do pamięci, gdy wyliczone ... przed wszelkimi iteracji pętli foreach. Nie ładuje on jednego wiersza podczas iteracji pętli foreach. Jeśli chcesz tego zachowania, użyj DataReader.

+0

Do tej pory faktycznie wyeksportowałem tabelę do płaskiego pliku i przeczytałem wiersz po wierszu. Następnym razem będę używał DataReadera jak profesjonalisty. :) – Gleno

6

Zadzwoń do 10GB mniej? masz dobre poczucie humoru!

Można rozważyć ładowanie wierszy w porcjach, np. Stronicowanie.

conn.DailyResults.Where(dr => dr.DailyTransactionTypeID == 1).Skip(x).Take(y); 
+0

Jeśli OP nie ma 20 GB pamięci RAM, jest to jedyny sposób na zajęcie się sytuacją. – Justin

+1

Nie jestem pewien, czy chcesz mi powiedzieć, że ta metoda stronicowania jest wydajna? Jestem tak zaskoczony, że IQueriable chce załadować rzeczy do pamięci. Chodzi mi o to, dlaczego nie uczynić z niej tablicy jakiegoś rodzaju, aby wskazać bezradnemu programiście jego paskudne intencje. :) – Gleno

2

Używanie programu DataReader jest krokiem do tyłu, chyba że istnieje sposób na użycie go w LINQ. Myślałem, że próbujemy uciec od ADO.

Rozwiązanie zasugerowane powyżej działa, ale jest naprawdę brzydkie. Tu jest mój kodu:

int iTake = 40000; 
int iSkip = 0; 
int iLoop; 
ent.CommandTimeout = 6000; 
while (true) 
{ 
    iLoop = 0; 
    IQueryable<viewClaimsBInfo> iInfo = (from q in ent.viewClaimsBInfo 
             where q.WorkDate >= dtStart && 
             q.WorkDate <= dtEnd 
             orderby q.WorkDate 
             select q) 
             .Skip(iSkip).Take(iTake); 
    foreach (viewClaimsBInfo qInfo in iInfo) 
    { 
    iLoop++; 
    if (lstClerk.Contains(qInfo.Clerk.Substring(0, 3))) 
    { 
      /// Various processing.... 
    } 
    } 
    if (iLoop < iTake) 
    break; 
    iSkip += iTake; 
} 

Widać, że muszę sprawdzić, że zabrakło zapisów, ponieważ pętla foreach zakończy się 40000 rekordów. Niedobrze.

Zaktualizowano 6/10/2011: Nawet to nie działa. Przy 2 000 000 rekordów otrzymuję wyjątek poza pamięcią. Jest również niesamowicie powolny. Kiedy zmodyfikowałem go, aby używał OleDB, działał przez około 15 sekund (w przeciwieństwie do 10+ minut) i nie zabrakło mu pamięci. Czy ktoś ma rozwiązanie LINQ, które działa i działa szybko?

+0

Nie jestem pewien, czy śledzę niektóre z dziwniejszych części, ale chodzi o to, że: = Query, Skip, Take.Fantastycznie, z wyjątkiem części, w której teraz masz inny problem - ile możesz wziąć. Również zapraszam do stackoverflow! : D – Gleno

+0

Gleno, dzięki. Nie jestem pewien, co uważasz za "dziwniejsze części", chociaż "dziwne" wydaje się być moje drugie imię. :) Wróciłem do ADO.Net, niestety, jak wspomniano. –

1

Sugerowałbym użycie SQL zamiast modyfikacji tych danych.

0

Zastosowanie .AsNoTracking() - mówi DbEntities nie buforować pobrane wiersze

using (var conn = new DbEntities() { CommandTimeout = 600*100}) 
{ 
    var dtable = conn.DailyResults 
       .AsNoTracking()  // <<<<<<<<<<<<<< 
       .Where(dr => dr.DailyTransactionTypeID == 1); 
    foreach (var dailyResult in dtable) 
    { 
     //Math here, results stored in-memory, but this table is very small. 
     //At the very least compared to stuff I already have in memory. :) 
    } 
} 
Powiązane problemy