EF Plus – aktualizacja wielu obiektów jednym zapytaniem

Wprowadzenie

Bardzo lubię wykorzystywać Entity Framework w swoich projektach. W szczególności za łatwość i szybkość tworzenia kodu (na przykład za opisywany ostatnio mechanizm migracji). Ale, żeby nie było tak różowo, Entity Framework ma również swoje problemy. Głównie są one związane z wydajnością. Szczególnie, że niektóre operacje, który wykonalibyśmy w czystym SQLu jednym prostym zapytaniem, w Entity Framework wymagają wielu operacji na bazie danych.

Taką operacji jest aktualizacja lub usuwanie wielu rekordów na podstawie jakiegoś warunku. Na przykład usunięcie wszystkich produktów z bazy, które są w określonej kategorii (kolumna CategoryId ma określoną wartość). W czystym Entity Framework nie możemy zrobić tego efektywnie. Ale z pomocą przychodzą nam dodatkowe biblioteki. Jedną z takich bibliotek jest Entity Framework Plus, która umożliwia między innymi aktualizacje/usuwanie wielu obiektów jednym zapytaniem.

Problem?

Wyobraźmy sobie, że potrzebujemy do aplikacji dodać funkcjonalność usuwania kategorii produktów. Mając na myśli usuwanie, nie chodzi mi o fizyczne usuniecie danych z bazy, tylko oznaczenie, że są usunięte (każdy rekord w bazie ma kolumnę IsActive). W przypadku usuwania kategorii chcemy również usunąć wszystkie produkty przypisane do danej kategorii.

W testowej aplikacji klasa dla kategorii oraz produktu wyglądają tak:

BaseModel jest klasą bazową dla wszystkich modeli. W przykładzie w niej znajduje się właściwość Id (klucz główny w tabeli) oraz właściwość IsActive określająca, czy obiekt jest usunięty, czy nie.

Usunięcie kategorii oraz powiązanych produktów w czystym Entity Framework może wyglądać mniej więcej tak:

W pierwszej kolejności pobieram kategorie z bazy (pierwszą aktywną kategorie, dzięki czemu przykład można go uruchamiać wielokrotnie). W przykładzie w metodzie Seed uzupełniane są dane testowe za pomocą biblioteki Bogus (10 kategorii oraz 100 produktów).

Następnie sprawdzam, czy kategoria została pobrania. Gdy tak to ustawiam jej właściwość IsActive na false, a następnie przechodzę po wszystkich powiązanych produktach i dla nich również ustawiam IsActive na false.

Na końcu wywołuje metodę SaveChanges, które aktualizuje dane w bazie.

Powyższy kod można by jeszcze trochę usprawnić np. dodają Include podczas pobierania kategorii, aby w jednym zapytaniu pobrać również produkty.  Ale tym momencie nie jest to główny problem, który chcemy rozwiązać.

Wynik działania powyższego w profilerze wygląda tak:

soft delete ef profiler

Ja widać, w pierwszej kolejności wykonały się dwa select, które pobrały kategorie oraz produktu. Później w transakcji są wykonywane updaty. Jeden dla kategorii, a pozostałe dla produktów – jedno zapytanie dla każdego produktu.

Tutaj właśnie widać jeden z problemów z Entity Framework. W przypadku takiej bardzo prostej aktualizacji danych, Entity Framework generuje dużo niepotrzebnych zapytań. Gdybym w bazie miał przypiętych 100 tyś produktów do kategorii, wtedy wykonało by się 100 tyś updatów mimo, że można zrobić to samo jednym zapytaniem.

Entity Framework Plus

Z pomocą w rozwiązaniu tego problemu przychodzi nam darmowa biblioteka Entity Framework Plus (https://entityframework-plus.net). Udostępnia ona między innymi funkcjonalność Batch Update, którą wykorzystam do rozwiązania naszego problemu.

Batch Update umożliwia wygenerowanie updatu w trochę inny sposób. Możemy przede wszystkich zapisać warunek, który efektywniej określi rekordy do aktualizacji niż przekazywanie poszczególnych identyfikatorów produktów. Najlepiej zobaczyć działanie Batch Update w kodzie:

Podobnie jak wcześniej pobieram kategorie oraz ustawiam jest właściwość IsActive na false. Natomiast kolejny krok jest już zupełnie inny. Tworze zapytanie Linq z Where, w którym określam jakie produkty mnie interesują (w określonej kategorii). I na końcu wywołuje nową metodę Update, która została dodana przez Entity Framework Plus. W niej określam jakie kolumny w bazie mają zostać zaktualizowane oraz na jaką wartość (ustawiamy IsActive na false).

Po uruchomieniu tego kod, w profilerze widać, że ilość zapytań dość mocno się zmniejszyła:

soft delete ef plus profiler

W tym momencie mamy tylko dwie komendy Update. Jedna aktualizuje kategorie oraz druga aktualizuje wszystkie produkty w jednym zapytaniu. W dolnej części zrzutu profilera widać jakie zapytanie zostało wygenerowane przez Entity Framework Plus. Nie jest ono idealne, ale dużo lepsze od tego co jest w zwykłym Entity Framework.

Darmowy kurs Visual Studio

Pracując z setkami programistów, zauważyłem, że większość osób nie pracuje efektywnie w Visual Studio. W skrajnych przypadkach korzystali z kopiowania z wykorzystaniem menu Edit. Wiem, że to dziwne, ale naprawdę niektórzy tak pracują. Dlatego postanowiłem stworzyć kurs Visual Studio – aby pomóc koleżankom i kolegom w efektywniejszej pracy.

Przygotowałem 20 lekcji e-mail, w których pokażę Ci, w jaki sposób pracować efektywniej i szybciej w Visual Studio. Poznasz dodatki, bez których nie wyobrażam sobie pracy w tym IDE.

Po więcej informacji zapraszam na dedykowaną stronę kursu: Darmowy Kurs Visual Studio.

Quiz C#

Ostatnio przygotowałem również quiz C#, w którym możesz sprawdzić swoją wiedzę. Podejmiesz wyzwanie?

Transakcje?

Analizując wykonane zapytania w profilerze, można zauważyć, że aktualizacja produktów wykonała się poza transakcją utworzoną przez metodę SaveChanges.

Tak faktycznie się dzieje i warto o tym pamiętać. Metoda Update od razu wykonuje zapytanie na bazie, bez czekania na wywołanie SaveChanges. Nawet niewywołanie SaveChanges spowoduje, że dane zostaną zaktualizowane przez metodę Update. Dlatego powyższy kod nie jest do końca poprawny i trzeba go trochę zmodyfikować , aby rozwiązać ten problem. Można dodać ręcznie transakcje, w której wykonają się zmiany z metody Update oraz SaveChanges:

Tym razem oba updaty są wykonywane w transakcji:

soft delete ef plus profiler with transaction

Usuwanie

Entity Framework Plus również wspiera Batch Delete. Działanie tego mechanizm jest bardzo podobne jak Batch Update. Równica polega na tym, że zamiast metody Update wywołujemy metodę Delete:

W tym przypadku również na bazie wykonuje się pojedyncze zapytanie:

delete ef plus profiler with transaction

Fakt jest już ono trochę bardziej rozbudowane.

Przykład

Na githubie (https://github.com/danielplawgo/EFPlusBatch) znajduje się przykład do tego wpisu. Po jego pobraniu należy ustawić w app.config poprawnego connection string. W klasie Program znajdują się poszczególne metody, które należy wywołać w metodzie Main.

Podsumowanie

Entity Framework jest bardzo fajną biblioteką do pracy z bazami danych. Ale warto pamiętać o niektórych problemach, które występując w pracy z nim. Na szczęście niektóre problemy można rozwiązać wykorzystując dodatkowe biblioteki takie jak Entity Framework Plus. Dzięki którym część operacji można wykonać dużo bardziej efektywnie.

Entity Framework Plus poza Batch Update oraz Batch Delete dostępnia też kilka innych ciekawych opcji, które opiszę w jednym z kolejnych wpisów.

Dodatkowo warto się jeszcze zainteresować płatną wersją biblioteki o nazwie Entity Framework Extensions – https://entityframework-extensions.net/

3 thoughts on “EF Plus – aktualizacja wielu obiektów jednym zapytaniem

  • Pingback: dotnetomaniak.pl
    • Przykład w tym momencie zawiera w bazie 10 rekordów, więc nie wyciągałbym z tego wniosków 🙂

      W wolnej chwili zmienię generowanie danych, aby Bogus generował kilka tysięcy produków i wtedy możemy porównać oba rozwiązania 🙂

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *