Почему добавление объектов в EF 4.1 так медленно по сравнению с ObjectContext?

В принципе, я вставляю 35000 объектов в одну транзакцию:

using(var uow = new MyContext()){
  for(int i = 1; i < 35000; i++) {
     var o = new MyObject()...;
     uow.MySet.Add(o);
  }
  uow.SaveChanges();
}

Это длится вечно! Если я использую базовый ObjectContex t (используя IObjectAdapter), он все же медленный, но занимает около 20 секунд. Похоже, что DbSet<> выполняет некоторые линейные поиски, что занимает квадратное количество времени...

Кто-нибудь еще видит эту проблему?

Ответ 1

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

context.Configuration.AutoDetectChangesEnabled = false;

Обнаружение изменений по умолчанию включено в API DbContext.

Причина, по которой DbContext ведет себя так же, как API ObjectContext, заключается в том, что многие другие функции API DbContext будут вызывать DetectChanges внутренне, чем функции API ObjectContext, когда включено автоматическое обнаружение изменений.

Здесь вы можете найти список тех функций, которые вызывают DetectChanges по умолчанию. Это:

  • Члены Add, Attach, Find, Local или Remove на DbSet
  • Члены GetValidationErrors, Entry или SaveChanges на DbContext
  • Метод Entries на DbChangeTracker

Особенно Add вызывает DetectChanges, который несет ответственность за плохую производительность, которую вы испытали.

Я противопоставляю это API ObjectContext API DetectChanges только автоматически в SaveChanges, но не в AddObject и других соответствующих методах, упомянутых выше. Это причина, по которой производительность по умолчанию ObjectContext выполняется быстрее.

Почему они представили это автоматическое обнаружение изменений по умолчанию в DbContext во множестве функций? Я не уверен, но кажется, что отключение его и вызов DetectChanges вручную в соответствующих точках рассматривается как расширенный и может легко вводить тонкие ошибки в ваше приложение, поэтому используйте [it] с заботиться.

Ответ 2

Маленький эмпирический тест с EF 4.3 CodeFirst:

Удалено 1000 объектов с помощью AutoDetectChanges = true: 23 с

Удалено 1000 объектов с помощью AutoDetectChanges = false: 11 секунд

Внесено 1000 объектов с помощью AutoDetectChanges = true: 21 sec

Внесено 1000 объектов с помощью AutoDetectChanges = false: 13 секунд

Ответ 3

В .netcore 2.0 это было перемещено в:

context.ChangeTracker.AutoDetectChangesEnabled = false;

Ответ 4

Помимо ответов, которые вы нашли здесь. Важно знать, что на уровне базы данных больше работы для вставки, чем для добавления. База данных должна расширять/распределять новое пространство. Затем он должен обновить, по крайней мере, индекс первичного ключа. Хотя индексы также могут обновляться при обновлении, это намного реже. Если есть какие-либо внешние ключи, он должен также прочитать эти индексы, чтобы убедиться в сохранности ссылочной целостности. Триггеры также могут играть роль, хотя они могут влиять на обновления таким же образом.

Вся эта работа с базами данных имеет смысл в повседневной активности вставки, созданной пользовательскими записями. Но если вы просто загружаете существующую базу данных или имеете процесс, который генерирует много вставок. Вы можете взглянуть на способы ускорения этого, отложив его до конца. Обычно отключение индексов при вставке является обычным способом. Существует очень сложная оптимизация, которая может быть выполнена в зависимости от случая, они могут быть немного подавляющими.

Просто знайте, что в целом вставка займет больше времени, чем обновления.