2010-06-16 2 views
6

У меня довольно сложный запрос linq к объектам, который я показываю на веб-сайте. Он использует пейджинг, поэтому я никогда не вытягиваю более 50 записей за раз для отображения.Как бороться с большими наборами результатов с Linq для Entities?

Но я также хочу предоставить пользователю возможность экспортировать полные результаты в Excel или какой-либо другой формат файла.

Я забочусь о том, что потенциально может быть большое количество записей, которые одновременно загружаются в память, чтобы сделать это.

Есть ли способ обработать набор результатов linq 1 запись за раз, как вы могли бы с datareader, так что только одна запись действительно хранится в памяти за раз?

Я видел предложения о том, что если вы перечислите запрос linq с циклом foreach, то все записи не будут сразу считываться в память и не будут перегружать сервер.

У кого-нибудь есть ссылка на что-то, что я мог прочитать, чтобы проверить это?

Буду признателен за любую помощь.

Благодаря

+2

результат не должен быть в памяти по умолчанию - вы что-то делаете (ToArray, ToList, что угодно), чтобы заставить все это занести в память? Если нет, просто перебирайте результаты (foreach, Select, whatever), и все должно быть в порядке. –

+1

В настоящее время мой метод запроса вызывает ToList() и возвращает список, но я мог бы изменить это легко, чтобы вернуть QueryObject. Вы уверены, что если я сделаю цикл foreach над IQueryable, он будет передавать данные? – user169867

+0

Итак, ваш 'ToList()' будет в основном делать что-то вроде 'List list = new Список (); foreach (объект объекта в запросе) {list.Add (объект); } '. 'Foreach' будет вызывать' query.MoveNext() '. Поэтому вы загружаете все в список и увеличиваете использование памяти. (Я пропустил элемент GetEnumerator() и т. Д., Так что это не так) –

ответ

5

set the ObjectContext to MergeOption.NoTracking (так как это только для чтения операции). Если вы используете тот же ObjectContext для сохранения других данных, Detach the object из контекста.

как отделить

foreach(IQueryable) 
{ 
    //do something 
    objectContext.Detach(object); 
} 

Edit: Если вы используете NoTracking вариант, нет необходимости отсоединять

edit2: Я написал Matt Warren об этом сценарии. И я разместить соответствующую частную корреспонденцию здесь, с его одобрением

Результаты сервера SQL может не даже все производится сервер еще. Запрос начался на сервере , и первая партия результатов передается клиенту, но не производится (или они кэшируются на сервере), пока клиент не попросит их прочитать. Это то, что называется «firehose курсором», или иногда упоминается как как потоковая передача. Сервер отправляет их так быстро, как это возможно, и клиент читает их так быстро, как это может (ваш код), но есть протокол данных передачи под этим требует подтверждения от клиента продолжать посылать больше данных.

С IQueryable наследует от IEnumerable, я считаю, что основной запрос отправляется на сервер будет то же самое. Однако, когда мы делаем IEnumerable.ToList(), устройство чтения данных, которое используется базовым соединением, начнет заполнять объект, объекты будут загружены в домен приложения и могут закончиться без памяти. Эти объекты еще не могут быть удалены.

Когда вы используете foreach и IEunmerable, считыватель данных считывает результат SQL по одному за раз, объекты создаются и затем удаляются. Основное соединение может получать данные в кусках и может не отправлять ответ на SQL Server до тех пор, пока все куски не будут прочитаны. Следовательно, вы не столкнетесь с «из memory` исключения

Edit3:

Когда ваш запрос работает, вы на самом деле можете открыть свой SQL Server„Activity Monitor“и увидеть запрос, Целевого государство как SUSPENDED и Wait Тип как Async_network_IO - который фактически утверждает, что результат указан в сетевом буфере SQL Server. Вы можете подробнее узнать об этом here и here

+0

Да, установка NoTracking on тоже должна помочь. Вы знаете, есть ли способ установить его на весь контекст? Я только что установил его на ObjectSet внутри содержимого. – user169867

+0

Вы ищете 'objectContext.MergeOption = MergeOption.NoTracking'? http://msdn.microsoft.com/en-us/library/bb738896.aspx – ram

+0

Я не думаю, что ObjectContext имеет свойство MergeOption. Вы думаете об ObjectQuery вместо этого? – user169867

1

Посмотрите на возвращаемое значение запроса LINQ. Он должен быть IEnumerable<>, который загружает только один объект за раз. Если вы затем используете что-то вроде .ToList(), все они будут загружены в память. Просто убедитесь, что ваш код не поддерживает список или использует более одного экземпляра за раз, и все будет в порядке.

Edit: Чтобы добавить к тому, что люди говорят о Еогеаспе ... Если вы делаете что-то вроде:

var query = from o in Objects 
      where o.Name = "abc" 
      select o; 

foreach (Object o in query) 
{ 
    // Do something with o 
} 

Часть запросов использует отложенное выполнение (see examples), так что объекты не находятся в память еще. Результат foreach выполняет итерацию по результатам, но получает только один объект за раз. query использует IEnumerator, который имеет Reset() и MoveNext(). Foreach вызывает MoveNext() каждый раунд, пока не будет больше результатов.

Смежные вопросы