web-dev-qa-db-ja.com

foreachを使用してIQueryableを反復処理すると、メモリ不足の例外が発生します

Foreach/IQueryableとLINQ-to-SQLを使用して、小さい(〜10GB)テーブルを反復処理しています。次のようになります。

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デバッガーは、foreachループのベースでしばらくすると、メモリ不足例外をスローします。 dtableの行はフラッシュされていないと思います。何をすべきか?

14
Gleno

IQueryable<DailyResult> dtableは、列挙されたときにクエリ結果全体をメモリにロードしようとします... foreachループが繰り返される前に。 foreachループの反復中に1行をロードしません。その動作が必要な場合は、 DataReader を使用してください。

16
Amy B

あなたは〜10GBを小さめと呼びますか?あなたは素晴らしいユーモアのセンスを持っています!

行をチャンクでロードすること、つまりページネーションを検討することもできます。

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

DataReaderの使用は、LINQ内で使用する方法がない限り、一歩後退します。 ADOから逃げようとしていると思いました。

上で提案された解決策は機能しますが、それは本当に醜いです。これが私のコードです:

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

Foreachループは40,000レコードで終了するため、レコードが不足していないかどうかを確認する必要があることがわかります。良くない。

2011年6月10日更新:これでも機能しません。 2,000,000レコード程度で、メモリ不足の例外が発生します。それはまた、途方もなく遅いです。 OleDBを使用するように変更した場合、(10分以上ではなく)約15秒で実行され、メモリが不足することはありませんでした。誰かがすばやく動作して実行されるLINQソリューションを持っていますか?

2
John Cole

代わりにSQLを使用してこのデータを変更することをお勧めします。

0
Seann Alexander

使用。AsNoTracking()-DbEntitiesに通知キャッシュしない取得した行

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