EF Plus – aktualizacja wielu obiektów jednym zapytaniem

Wprowadzenie

Bardzo lubię wykorzystywać Entity Framework w swoich projektach. W szczególności lubię je 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. Są one głównie 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ą operacją 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. Z pomocą przychodzą nam jednak 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 dodać do aplikacji funkcjonalność usuwania kategorii produktów. Mając na myśli usuwanie, nie chodzi mi o fizyczne usunięcie 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 klasy 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ą kategorię, dzięki czemu przykład można 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 pobrana. Gdy została, 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łuję metodę SaveChanges, które aktualizuje dane w bazie.

Powyższy kod można by jeszcze nieco usprawnić, np. dodając 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 powyższego działania w profilerze wygląda tak:

soft delete ef profiler

Jak widać, w pierwszej kolejności wykonały się dwa selecty, które pobrały kategorie oraz produkty. Później w transakcji są wykonywane update’y. 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 wiele niepotrzebnych zapytań. Gdybym w bazie miał przypiętych 100 tysięcy produktów do kategorii, wtedy wykonało by się 100 tysięcy update’ó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 update’u w nieco inny sposób. Możemy przede wszystkim zapisać warunek, który efektywniej określi rekordy do aktualizacji, niż ma to miejsce w przypadku przekazywania poszczególnych identyfikatorów produktów. Najlepiej zobaczyć działanie Batch Update w kodzie:

Podobnie jak wcześniej pobieram kategorie oraz ustawiam właściwość IsActive na false. Natomiast kolejny krok jest już zupełnie inny. Tworzę zapytanie Linq z Where, w którym określam, jakie produkty mnie interesują (w określonej kategorii). I na końcu wywołuję 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 kodu w profilerze widać, że liczba zapytań dość mocno się zmniejszyła:

soft delete ef plus profiler

W tym momencie mamy tylko dwie komendy Update. Jedna aktualizuje kategorie, a 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 jest 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 nieco zmodyfikować , aby rozwiązać ten problem. Można dodać ręcznie transakcję, w której wykonają się zmiany z metody Update oraz SaveChanges:

Tym razem oba update’y są wykonywane w transakcji:

soft delete ef plus profiler with transaction

Usuwanie

Entity Framework Plus również wspiera Batch Delete. Działanie tego mechanizmu jest bardzo podobne jak w przypadku Batch Update. Różnica 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 stringa. 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ą 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 nim część operacji można wykonać dużo bardziej efektywnie.

Entity Framework Plus poza Batch Update oraz Batch Delete udostę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 *