2010-09-10 6 views
10

Ich bin durch eine kleinere (~ 10 GB) Tabelle mit einem Foreach/IQueryable und LINQ-SQL. sieht ungefähr so ​​aus:Iterieren durch IQueryable mit foreach Ergebnisse in einem nicht genügend Speicher Ausnahme

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. :) 
    } 
} 

Das Visual Studio-Debugger führt eine Out-of-Speicher Ausnahme nach kurzer Zeit auf der Basis der foreach-Schleife. Ich gehe davon aus, dass die Zeilen der dtable nicht geleert werden. Was ist zu tun?

+0

zwischenzuspeichern, was haben Sie bereits im Speicher abgelegt, die größer als 10 GB ist? Meinst du 10 MB? – msarchet

+0

Ich habe 16 GB Speicher auf diesem Computer, aber mindestens die Hälfte davon wird von Windows Bloat plus SQL-Caches verwendet. Ich konnte 10 GB Speicher nicht speichern, also lief ich davon. Ich bin überrascht, dass IQueryable die gesamte Tabelle abruft ... Ich würde erwarten, dass es eine oder eine kleine Anzahl von Zeilen gleichzeitig holt. – Gleno

+0

Ich bin in der Lage, dies etwas zu umgehen, indem ich das Kompilierungsziel auf x64 anstelle von x86 ändere, was mehr Speicher auf meinem Rechner nutzt. Die Daten, die ich in meiner foreach-Schleife durchforste, sind jedoch nicht riesig, also denke ich, dass die Sachen in der Schleife nicht richtig gesammelt werden. –

Antwort

12

Die IQueryable<DailyResult> dTabelle wird versuchen, das gesamte Abfrageergebnis in dem Speicher zu laden, wenn aufgezählt ... vor einer Wiederholung der foreach-Schleife. Während der Iteration der foreach-Schleife wird keine Zeile geladen. Wenn Sie dieses Verhalten wünschen, verwenden Sie DataReader.

+0

Inzwischen habe ich die Tabelle tatsächlich in eine flache Datei exportiert und Zeile für Zeile gelesen. Nächstes Mal werde ich DataReader wie ein Profi verwenden. :) – Gleno

6

Sie nennen ~ 10GB kleiner? Du hast einen guten Sinn für Humor!

Sie könnten Zeilen in Blöcken laden, auch bekannt als Paginierung.

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

Wenn das OP nicht über 20 GB RAM verfügt, ist dies die einzige Möglichkeit, mit der Situation umzugehen. – Justin

+1

Ich bin nicht sicher, wollen Sie mir sagen, dass diese Umbruchmethode effizient ist? Ich bin nur so überrascht, dass IQueriable Sachen in den Speicher laden will. Ich meine, warum sollte man es nicht zu einer Art Array machen, um dem hilflosen Programmierer über seine üblen Absichten zu berichten? :) – Gleno

2

Die Verwendung von DataReader ist ein Schritt zurück, es sei denn, es gibt eine Möglichkeit, sie innerhalb von LINQ zu verwenden. Ich dachte, wir wollten von ADO wegkommen.

Die oben vorgeschlagene Lösung funktioniert, aber es ist wirklich hässlich. Hier ist mein Code:

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; 
} 

Sie können sehen, dass ich habe aus Aufzeichnungen laufen zu überprüfen, da die foreach-Schleife bei 40.000 Aufzeichnungen enden wird. Nicht gut.

Aktualisiert am 10.06.2011: Auch das funktioniert nicht. Bei etwa 2.000.000 Datensätzen bekomme ich eine Ausnahme wegen Speichermangel. Es ist auch quälend langsam. Als ich es änderte, um OleDB zu verwenden, lief es in ungefähr 15 Sekunden (im Gegensatz zu 10+ Minuten) und der Speicher ging nicht aus. Hat jemand eine LINQ-Lösung, die schnell funktioniert und läuft?

+0

Ich bin mir nicht sicher, dass ich einige der seltsameren Teile folgen, aber die Idee ist => Query, Skip, Take.Fantastisch, abgesehen von dem Teil, wo Sie jetzt ein anderes Problem bekommen - wie viel zu nehmen. Willkommen bei stackoverflow! : D – Gleno

+0

Gleno, danke. Ich bin nicht sicher, was Sie die "seltsameren Teile", obwohl "seltsam" scheint mein zweiter Vorname zu sein. :) Ich ging leider, wie gesagt, zurück zu ADO.Net. –

1

Ich würde vorschlagen, SQL stattdessen zu verwenden, um diese Daten zu ändern.

0

Verwenden .AsNoTracking() - es sagt DbEntities nicht abgerufenen Zeilen

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. :) 
    } 
} 
Verwandte Themen