2017-01-25 7 views
1

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

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

Я бегу что-то простое, как это (Это было упрощено ALOT для целей данного вопроса):

var quoteList = DbContext.Quotes.ToList(); 

List<QuotesItem> quoteViewItemList = new List<QuotesItem>(); 

quoteList.ForAll(quote => { 
    var quoteViewItem = new QuotesItem(); 
    quoteViewItem.YearEnding = quote.QuoteService.FirstOrDefault().Yearending; //is SOMETIMES null when it shouldn't be. 
    quoteViewItem.Manager quote.Client.Manager.Name; //IS sometimes null 
    quoteViewItem.... = ..... // More processing here 
    quoteViewItemList.Add(quoteViewItem); 
}); 

проблема является QuoteService кажется утратившими иногда даже тогда, когда его не равно нулю в списке.

+0

Не 'quotList' a' Список '? Если это так, у него не будет члена, называемого 'QuoteService'. Вы имели в виду 'quote.QuoteService .....'? – juharr

+0

да, это ошибка. Я исправляю это сейчас. – michael

ответ

0

Сначала давайте поговорим о проблеме. Единственная причина, по которой я могу изобразить, - это избавиться от DbContext befour . ForAll финишировал своей работой, возможно, из другой темы? Но на данный момент я просто догадываюсь.

Я могу дать вам пару предложений о возможной оптимизации кода. Сначала оценка всех котировок с использованием .ToList(), вероятно, имеет некоторое влияние на производительность, если в базе данных много записей, поэтому мой совет заключается в обмене с нетерпением оценкой с помощью курсора SQL. Это может быть достигнуто с использованием любой конструкции/кода, которая использует внутри себя IEnumerable <> и более конкретно .GetEnumerator().

Внутренняя реализация IQueriable <> в EF создает курсор SQL, который довольно быстро. Самое приятное то, что вы можете использовать его с .AsParallel() или Parallel.ForEach, как внутри, так и внутри Enumerators.

Это не очень документированная функция, но если вы запустите профиль SQL Server и выполните приведенный ниже код, вы увидите, что на сервер выполняется только один запрос, а также вы можете заметить, что ОЗУ машины, выполняющей код, не шип, что означает, что он не получает все сразу.

Я нашел некоторую случайную информацию о нем, если вы заинтересуете: https://sqljudo.wordpress.com/2015/02/24/entity-framework-hidden-cursors/

Хотя я использовал этот подход в тех случаях, когда код работает на записи базы данных был довольно тяжелым для простой проекции от одного тип к другому, запустив курсор с помощью .AsParallel(..) или просто foreach.

Таким образом, используя

DbContext.Quotes 
.AsParallel() 
.WithDegreeOfParallelism(...) 
.ForAll(...) 

должен работать с хорошей производительностью.

Мой второй совет касается доступа к навигационным свойствам в EF с ленивой загрузкой. Это, как вы знаете, приводит к проблеме выбора N + 1. Таким образом, вместо того, чтобы зависеть от EF ленивой оценки в этом случае это будет лучше охотно получать навигационные свойства, поэтому мы можем переписать код примерно как это

DbContext.Quotes 
    .Include(x => x.QuoteService) 
    .AsParallel() 
    .WithDegreeOfParallelism(...) 
    .ForAll(...) 

Таким образом, при доступе к QuoteService навигации свойства EF выиграл» t делать какие-либо дополнительные запросы к Серверу, поэтому это должно значительно улучшить вашу производительность, и, возможно, он сможет исправить исправление нулевой ссылки (я надеюсь)

Общая версия метода .Include (..), который я использую часть пространства имен System.Data.Entity.

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

var queryViewItems = DbContext.Quotes 
      .AsNoTracking() 
      .Include(x => x.QuoteService) 
      .AsParallel() 
      .WithDegreeOfParallelism(...) 
      .Select(x => { ... }) 
      .ToList(); 

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

  • Нет связанных вопросов^_^