使用foreach迭代IQueryable会导致内存不足异常

时间:2010-09-10 20:45:18

标签: c# linq linq-to-sql

我正在使用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的行没有被刷新。该怎么办?

5 个答案:

答案 0 :(得分:14)

IQueryable<DailyResult> dtable将尝试在枚举时...在foreach循环的任何迭代之前将整个查询结果加载到内存中。它在foreach循环的迭代期间不加载一行。如果您想要这种行为,请使用DataReader

答案 1 :(得分:6)

你打电话~10GB小吗?你有一种很好的幽默感!

您可以考虑以块(也称为分页)加载行。

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

答案 2 :(得分:2)

使用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时,它运行大约15秒(而不是10分钟以上)并且没有内存耗尽。有没有人有LINQ解决方案可以运行并快速运行?

答案 3 :(得分:0)

我建议使用SQL来修改这些数据。

答案 4 :(得分:0)

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