Подтвердить что ты не робот

Итерация через IQueryable с помощью foreach приводит к исключению из памяти

Я повторяю небольшую таблицу (~ 10 ГБ) с помощью foreach/IQueryable и LINQ-to-SQL. Выглядит примерно так:

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 не очищаются. Что делать?

4b9b3361

Ответ 1

IQueryable<DailyResult> dtable попытается загрузить весь результат запроса в память при перечислении... перед любыми итерациями цикла foreach. Он не загружает одну строку во время итерации цикла foreach. Если вы хотите этого поведения, используйте DataReader.

Ответ 2

Вы называете ~ 10 ГБ маленьким? у вас хорошее чувство юмора!

Вы можете рассмотреть возможность загрузки строк в куски, а также разбиение на страницы.

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

Ответ 3

Использование 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 записей. Нехорошо.

Обновлено 6/10/2011: Даже это не работает. При 20000000 записей или около того я получаю исключение из памяти. Это также мучительно медленно. Когда я модифицировал его для использования OleDB, он запустил около 15 секунд (в отличие от 10 + минут) и не исчерпал память. У кого-нибудь есть решение LINQ, которое работает и работает быстро?

Ответ 4

Я бы предложил использовать SQL вместо этого, чтобы изменить эти данные.

Ответ 5

Используйте .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. :)
     }
}