Dlaczego wstawianie jednostek w EF 4,1 jest tak wolne w porównaniu do ObjectContext?

81

Zasadniczo wstawiam 35000 obiektów w ramach jednej transakcji:

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

To trwa wieczność! Jeśli użyję podstawowego ObjectContext (używając IObjectAdapter), nadal jest powolny, ale zajmuje około 20 sekund. Wygląda na DbSet<>to, że przeprowadza wyszukiwanie liniowe, które zajmuje kwadratową ilość czasu ...

Czy ktoś jeszcze widzi ten problem?

Hartmuta
źródło
3
Jakoś wierzę, że odpowiedź będzie podobna do tej: stackoverflow.com/questions/5917478/…
Ladislav Mrnka

Odpowiedzi:

128

Jak już wskazał Ladislav w komentarzu, musisz wyłączyć automatyczne wykrywanie zmian, aby poprawić wydajność:

context.Configuration.AutoDetectChangesEnabled = false;

To wykrywanie zmian jest domyślnie włączone w DbContextinterfejsie API.

Powodem, dla którego DbContextzachowuje się tak inaczej niż ObjectContextAPI, jest to, że o wiele więcej funkcji DbContextAPI będzie wywoływać DetectChangeswewnętrznie niż funkcje ObjectContextAPI, gdy włączone jest automatyczne wykrywanie zmian.

Tutaj znajdziesz listę tych funkcji, które są wywoływane DetectChangesdomyślnie. Oni są:

  • Te Add, Attach, Find, Local, lub Removeczłonków wDbSet
  • Członkowie GetValidationErrors, Entrylub SaveChangeswDbContext
  • EntriesMetoda naDbChangeTracker

Szczególnie Addpołączenia, DetectChangesktóre są odpowiedzialne za słabą wydajność, jakiej doświadczyłeś.

W przeciwieństwie do tego ObjectContextAPI wywołuje DetectChangestylko automatycznie, SaveChangesale nie w, AddObjecti inne odpowiednie metody wymienione powyżej. To jest powód, dla którego domyślna wydajność ObjectContextjest szybsza.

Dlaczego wprowadzili to domyślne automatyczne wykrywanie zmian DbContextw tak wielu funkcjach? Nie jestem pewien, ale wydaje się, że wyłączenie go i DetectChangesręczne wywoływanie we właściwych punktach jest uważane za zaawansowane i może łatwo wprowadzić subtelne błędy do twojej aplikacji, więc używaj go ostrożnie .

Slauma
źródło
@Ladislav: Masz rację Nie znalazłem tego, ponieważ szukałem tylko problemów ze wstawianiem :-(
Hartmut
Dziękuję za wyjaśnienie. Właściwie wywołałem context.Configuration.AutoDetectChangesEnabled = false, ale zrobiłem to podczas konstrukcji bazy danych w metodzie Seed (). Myślałem, że to ustawi domyślne. Nie zdawałem sobie sprawy, że muszę to wywoływać w każdym przypadku. Dzięki!
Hartmut
3
@Hartmut: Możesz wyłączyć wykrywanie zmian w konstruktorze twojego pochodnego DbContext, wtedy masz je zawsze wyłączone. Ale osobiście w jakiś sposób denerwuje mnie ta uwaga o „potencjalnie wprowadzaniu subtelnych błędów”, gdy jest wyłączona. Domyślnie mam włączone wykrywanie zmian i wyłączam je tylko w blokach kodu takich jak twój, gdzie wzrost wydajności jest oczywisty i gdzie czuję się bezpiecznie, że nie powoduje problemów.
Slauma
Zgadzam się, testowałem tylko część mojej aplikacji, która ma kluczowe znaczenie dla wydajności. W kodzie produkcyjnym najlepiej jest ograniczyć to do przypadków, takich jak wkładki zbiorcze itp.
Hartmut
Dziękuję za tę odpowiedź. Jest wiele informacji, ale to nie koniec!
Fred Wilson
12

Mały test empiryczny z EF 4.3 CodeFirst:

Usunięto 1000 obiektów z AutoDetectChanges = true: 23 sek

Usunięto 1000 obiektów z AutoDetectChanges = false: 11 sek

Wstawiono 1000 obiektów z AutoDetectChanges = true: 21 sek

Wstawiono 1000 obiektów z AutoDetectChanges = false: 13 sek

Zax
źródło
1
Dzięki Zax. Jakie są twoje wyniki z 35 000 zgodnie z pytaniem? Zobaczysz, że w pierwotnym pytaniu oznacza to, że wydajność spada kwadratowo
Daniel Dyson,
9

W .netcore 2.0 zostało to przeniesione do:

context.ChangeTracker.AutoDetectChangesEnabled = false;

Maxvt
źródło
1

Oprócz odpowiedzi, które znalazłeś tutaj. Ważne jest, aby wiedzieć, że na poziomie bazy danych więcej pracy trzeba wstawić niż dodać. Baza danych musi rozszerzyć / przydzielić nowe miejsce. Następnie musi zaktualizować przynajmniej indeks klucza podstawowego. Chociaż indeksy mogą być również aktualizowane podczas aktualizacji, jest to znacznie mniej powszechne. Jeśli są jakieś klucze obce, musi również odczytać te indeksy, aby zapewnić zachowanie integralności referencyjnej. Wyzwalacze również mogą odgrywać rolę, chociaż mogą wpływać na aktualizacje w ten sam sposób.

Cała ta praca z bazą danych ma sens w codziennej czynności wstawiania zapoczątkowanej przez wpisy użytkowników. Ale jeśli po prostu przesyłasz istniejącą bazę danych lub masz proces, który generuje wiele wstawek. Możesz spojrzeć na sposoby na przyspieszenie tego, odkładając to na koniec. Zwykle wyłączanie indeksów podczas wstawiania jest powszechnym sposobem. Istnieją bardzo złożone optymalizacje, które można wykonać w zależności od przypadku, mogą one być nieco przytłaczające.

Po prostu wiedz, że generalnie wstawianie zajmie więcej czasu niż aktualizacje.

Arturo Hernandez
źródło