2011-08-13 14 views
30

Ten sam temat został omówiony tutaj 8 miesięcy temu: How do I speed up DbSet.Add()?. Nie zaproponowano rozwiązania innego niż użycie SqlBulkCopy, co jest dla nas nie do przyjęcia. Postanowiłem raz jeszcze poruszyć tę kwestię, mając nadzieję, że pojawią się nowe myśli i pomysły dotyczące tego problemu i zaproponowane zostaną inne rozwiązania. Przynajmniej jestem po prostu ciekawy, dlaczego ta operacja trwa tak długo.Dlaczego DbSet.Add działa tak wolno?

Problem polega na tym, że muszę zaktualizować jednostki 30K do bazy danych (EF 4.1, POCO). Typ encji jest dość prosty i zawiera liczbę całkowitą Id + pozostałe 4 właściwości liczb całkowitych bez relacji z innymi typami. 2 przypadki:

  • wszystkie to nowe rekordy. Uruchamianie context.Entities.Add (entity), jeden po drugim dla każdej jednostki, zajmuje 90 sekund z Cntx.Configuration.AutoDetectChangesEnabled = false (true value powoduje, że działa ona zawsze). Następnie SaveChanges zajmuje tylko sekundę. Inne podejście: dołączenie go do kontekstu jak to ma taką samą 90 sek:

    Cntx.Entities.Attach(entity); 
    Cntx.Entry(entity).State = EntityState.Added; 
    
  • wszystko im są istniejące rekordy z pewnymi zmianami. W przypadku trwa zaledwie kilka milisekund, aby dołączyć go do istniejącego kontekstu danych tak:

    Cntx.Entities.Attach(entity); 
    Cntx.Entry(entity).State = EntityState.Modified; 
    

    Zobacz różnicę?

Co kryje się za sceną metody Add, która sprawia, że ​​działa ona tak niesamowicie wolno?

+0

Połóż aktualizację jako odpowiedź proszę. –

+0

Próbowałem, ale nie mogłem tego zrobić, potrzebnych było 100 punktów reputacji (9 więcej), aby uzyskać odpowiedź samemu. – YMC

+1

Wznowiłem twoje pytanie, teraz masz 101 powtórzeń :) – Slauma

Odpowiedz

27

Mam ciekawe wyniki testów wydajności i znalazłem winnego. Nie widziałem żadnych takich informacji w żadnym źródle EF, jakie kiedykolwiek czytałem.

Okazuje się, że są one nadpisane w klasie bazowej. Klasa bazowa powinna zawierać właściwość Id współdzieloną między wszystkimi typami konkretnych elementów. To podejście zalecane przez wiele książek EF i całkiem dobrze znane. Można go znaleźć na przykład: How to best implement Equals for custom types?

Dokładniej, wydajność jest zabijana przez operację rozpakowywania (obiekt do konwersji typu betonu), która sprawiała, że ​​działa ona tak wolno. Kiedy skomentowałem tę linię kodu, zajęło 3 sekundy, aby uruchomić przeciwnika do 90 sekund wcześniej!

Kiedy ją znalazłem, zacząłem zastanawiać się, co może być alternatywą dla tych równań. Pierwszym pomysłem było wdrożenie IEquatable for EntityBase, ale zdarzyło się, że w ogóle się nie uruchamiał. Więc ostatecznie zdecydowałem się wdrożyć IEquatable dla każdej konkretnej klasy encji w moim modelu. Mam ich tylko kilka, więc jest to dla mnie niewielka aktualizacja. Możesz umieścić całą operację równościową (zwykle jest to porównanie 2 obiektów) w metodę rozszerzenia, aby współużytkować między konkretnymi klasami jednostek i uruchomić ją w następujący sposób: Równa ((EntityBase) ConcreteEntityClass). Najciekawsze, to IEquatable przyspiesza EntitySet.Add 6 razy!

Nie mam więcej problemów z wydajnością, ten sam kod działa dla mnie z mniej niż sekundą. Mam 180-krotny wzrost wydajności! Niesamowity!

Wnioski:

  1. najbardziej szybki sposób uruchomić EntitySet.Add jest mieć IEquatable dla konkretnego podmiotu (0,5 sek)
  2. Brakujący IEquatable czyni go uruchomić 3 sek.
  3. Having equals (obiekt obj), które większość źródeł zalecane czyni go uruchomić 90 sek
+2

@YMC: Czy mógłbyś rozwinąć implementację 'IEquatable '? Jestem zdezorientowany, czy zastąpić object.Equals() dla typu zmiennego (ponieważ masz również zastąpić object.GetHashCode(), ale jeśli kod mieszania zmienia się, gdy obiekt jest w słowniku, staje się osierocony). Po tym, jak obiekt zostanie utrwalony, mogę użyć klucza podstawowego z DB, ale zanim będzie on trwał, wszystkie nowe obiekty mają klucz 0. Postawiłem to jako osobne pytanie, ale nie otrzymałem jeszcze dobrej odpowiedzi http: // stackoverflow. com/questions/9782235/implement-iequatable-for-poco –

+0

@ YMC Mam również nadzieję, że omówisz implementację 'IEquatable '. Występuję w tym samym problemie. – vlad

+0

vlad i Eric, teraz nie mam dostępu do kodu, ale myślę, że możesz użyć co najmniej 2 podejść: 1) Guid do generowania podstawowych wartości klucza i implementacja GetHashCode() może być tak prosta jak coś w rodzaju 'return id. GetHashCode() '2) W przypadku identyfikatora całkowitoliczbowego może to wyglądać tak:' IsNew? base.GetHashCode(): Id.GetHashCode() ', IsNew zwraca wartość true, jeśli Id == 0. Popraw mnie, jeśli coś jest nie tak w aplikacji – YMC

Powiązane problemy