Чому вставка сутностей у 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внутрішньо, ніж функції ObjectContextAPI, коли ввімкнено автоматичне виявлення змін.

Тут ви можете знайти перелік тих функцій, які викликаються DetectChangesза замовчуванням. Вони є:

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

Особливо Addдзвінки, DetectChangesякі відповідають за погану роботу, яку ви пережили.

На відміну від цього, ObjectContextAPI викликає DetectChangesлише автоматично в, SaveChangesале не в, AddObjectта інші відповідні методи, згадані вище. Це причина, чому швидкість роботи за замовчуваннямObjectContext швидша.

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


@Ladislav: Ви маєте рацію, я не знайшов цього, оскільки шукав лише проблеми зі вставкою :-(
Хартмут,

Дякую за пояснення. Я насправді викликав context.Configuration.AutoDetectChangesEnabled = false, але я зробив це під час побудови бази даних у методі Seed (). Я думав, що це встановить за замовчуванням. Я не знав, що мушу називати це для кожного випадку. Дякую!
Хартмут

3
@Hartmut: Ви можете вимкнути виявлення змін всередині конструктора вашого похідного DbContext, тоді його завжди вимкнено. Але особисто мене якось нервує це зауваження про "потенційне впровадження тонких помилок", коли воно вимкнено. Я за замовчуванням увімкнув виявлення змін і вимикаю його лише в таких блоках коду, як ваш, де підвищення продуктивності очевидне і де я відчуваю себе в безпеці, щоб це не викликало проблем.
Слаума

Я згоден, я тестував лише деяку критично важливу частину моєї програми. У виробничому коді найкраще обмежити його такими випадками, як сипучі вставки тощо
Хартмут,

Дякую за цю відповідь. Багато інформації, але це переслідує!
Фред Вілсон,

12

Невеликий емпіричний тест з EF 4.3 CodeFirst:

Вилучено 1000 об’єктів за допомогою функції AutoDetectChanges = true: 23 сек

Вилучено 1000 об’єктів за допомогою AutoDetectChanges = false: 11 сек

Вставлено 1000 об’єктів із функцією AutoDetectChanges = true: 21 сек

Вставлено 1000 об’єктів із функцією AutoDetectChanges = false: 13 сек


1
Дякую Закс. Які ваші результати з 35 000 за запитанням? Ви побачите, що в оригінальному питанні видно, що ефективність падає квадратично
Деніел Дайсон,

9

У .netcore 2.0 це було переміщено до:

context.ChangeTracker.AutoDetectChangesEnabled = false;


1

Окрім відповідей, які ви знайшли тут. Важливо знати, що на рівні бази даних більше роботи вставити, ніж додати. База даних повинна розширити / виділити новий простір. Тоді він повинен оновити принаймні індекс первинного ключа. Хоча індекси можуть також оновлюватися під час оновлення, це набагато рідше. Якщо є зовнішні ключі, він також повинен прочитати ці індекси, щоб переконатися, що референтна цілісність збережена. Тригери також можуть зіграти свою роль, хоча вони можуть впливати на оновлення однаково.

Вся ця робота з базою даних має сенс у щоденній вставці, яку створюють записи користувачів. Але якщо ви просто завантажуєте існуючу базу даних або у вас є процес, який генерує багато вставок. Можливо, ви захочете розглянути способи пришвидшення цього, відклавши його до кінця. Зазвичай вимкнення індексів під час вставки є звичним способом. Існують дуже складні оптимізації, які можна зробити залежно від конкретного випадку, вони можуть бути дещо переважними.

Тільки знайте, що загалом вставка займе більше часу, ніж оновлення.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.