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

81

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

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

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

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

Хартмут
источник
3
Мне почему-то кажется, что ответ будет примерно таким: stackoverflow.com/questions/5917478/…
Ладислав Мрнка,

Ответы:

128

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

context.Configuration.AutoDetectChangesEnabled = false;

Это обнаружение изменений включено по умолчанию в DbContextAPI.

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

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

  • В Add, Attach, Find, Local, или Removeчлены наDbSet
  • Участники GetValidationErrors, Entryили SaveChangesнаDbContext
  • EntriesСпособ поDbChangeTracker

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

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

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

Slauma
источник
@Ladislav: Вы правы, я не нашел этого, так как искал только проблемы со вставкой :-(
Хартмут
Спасибо за объяснение. На самом деле я звонил context.Configuration.AutoDetectChangesEnabled = false, но я делал это во время построения базы данных в методе Seed (). Я думал, это установит значение по умолчанию. Я не знал, что должен вызывать это для каждого экземпляра. Благодаря!
Хартмут
3
@Hartmut: вы можете отключить обнаружение изменений внутри конструктора вашего производного DbContext, тогда оно всегда будет отключено. Но лично меня почему-то это замечание о "потенциально внесении мелких ошибок" при отключении заставляет меня нервничать. У меня по умолчанию включено обнаружение изменений, и я отключаю его только в таких блоках кода, как ваш, где прирост производительности очевиден и где я уверен, что это не вызовет проблем.
Slauma
Я согласен, я тестировал только некоторую критическую для производительности часть своего приложения. В производственном коде лучше ограничить его такими случаями, как объемные вставки и т. Д.
Хартмут
Спасибо за этот ответ. Там много информации, но это только начало!
Фред Уилсон
12

Небольшой эмпирический тест с EF 4.3 CodeFirst:

Удалено 1000 объектов с AutoDetectChanges = true: 23 сек.

Удалено 1000 объектов с AutoDetectChanges = false: 11 сек.

Вставлено 1000 объектов с AutoDetectChanges = true: 21 сек.

Вставлено 1000 объектов с AutoDetectChanges = false: 13 сек.

Zax
источник
1
Спасибо, Закс. Каковы ваши результаты с 35 000 согласно вопросу? Вы увидите, что в исходном вопросе указано, что производительность падает квадратично
Дэниел Дайсон
9

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

context.ChangeTracker.AutoDetectChangesEnabled = false;

Максвт
источник
1

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

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

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

Артуро Эрнандес
источник