Entity Framework Core

Entity Framework Core jest najnowszą wersją platformy, która pojawiła się po Entity Framework 6.4.x. Jest to otwarta, lekka, rozszerzalna i multiplatformowa wersja technologii dostępu do danych.

Tak jak wspomniałem we wprowadzeniu, Entity Framework jest sposobem odwzorowania obiektowej architektury systemu informatycznego na bazę danych o relacyjnym charakterze (ORM). Technologia tak jest rozszerzeniem ADO.NET, która daje programistom zautomatyzowany mechanizm dostępu i przechowywania danych w bazie danych.

EF Core jest przeznaczony do stosowania z aplikacjami .NET Core. Może być również wykorzystany w aplikacjach opartych o .NET Framework 4.5+.

Zanim pójdziemy dalej spójrzmy na poniższy diagram ilustrujący obsługiwane typy aplikacji oraz systemy operacyjne: EF Core: wsparcie różnych typów aplikacji

Krótka historia i spojrzenie w przyszłość

Zanim przejdziemy dalej spojrzymy nieco w historię EF Core oraz spróbujemy zobaczyć co przyniosą nam kolejne lata. Te informacje są istotne jeżeli już tworzymy aplikacje przy wykorzystaniu EF Core. Pierwsze wersje nie są już wspierane i zalecane jest przejście do najnowszych wydań. Spójrzmy zatem na poniższe zestawienie:

  • EF Core 5.0: wsparcie do połowy lutego 2022 roku;
  • EF Core 3.1: wsparcie do 3 grudnia 2022 roku (LTS );
  • EF Core 3.0: wsparcie wygasło 3 marca 2020 roku;
  • EF Core 2.2: wsparcie wygasło 23 grudnia 2019 roku;
  • EF Core 2.1: wsparcie do 21 sierpnia 2021 roku (LTS);
  • EF Core 2.0: wsparcie wygasło 1 października 2018 roku;
  • EF Core 1.1: wsparcie wygasło 27 czerwca 2019 roku;
  • EF Core 1.0: wsparcie wygasło 27 czerwca 2019 roku;

Po więcej szczegółowych informacji odsyłam do oficjalnej dokumentacji: https://docs.microsoft.com/pl-pl/ef/core/what-is-new/

Podejścia projektowe

EF Core wspiera dwa podejścia:

  1. Code-First
  2. Database-First

Sam Framework jest głównie skoncentrowany na pierwszym podejściu. Drugie z nich jest również obsługiwane ale w ograniczonym zakresie. Wynika to głównie z braku wsparcia narzędzi takich jak visual designer oraz wizard (kreator) dla generowania modelu bazy danych. Jeżeli jednak preferujecie to podejście możecie skorzystać z rozszerzeń, które dodają brakujące funkcjonalności: EF Core – narzędzia i rozszerzenia.

Tytułem wyjaśnienia: w przypadku pierwszego podejścia EF Core API tworzy bazę danych i tabele używając migracji opartej na konwencjach i konfiguracji dostarczonej w klasie kontekstowej. Podejście to jest użyteczne gdy tworzymy aplikacje w oparciu o DDD: C# - Domain Driven Design

W drugim podejściu EF Core API tworzy klasy domenowe i kontekstowe w oparciu o istniejącą bazę danych przy użyciu poleceń EF Core. W dalszej części cyklu zaczniemy od tego podejścia ponieważ początkowa złożoność będzie nieco mniejsza. Poszczególne składowe technologii takie jak konfiguracje czy Fluent API chciałbym omówić o osobnych wpisach. EF Core: wsparcie różnych typów aplikacji

Które podejście wybrać? Wielokrotnie nie mamy żadnego wyboru...

Pozwólcie na jeden przykład. Wyobraźcie sobie ogromny system typu Enterprise, który wykonuje złożone zadania biznesowe. System taki powstaje od 10 lat, jest ciągle rozbudowywany o nowe funkcjonalności a ilość tabel przekracza sto. Dodatkowo logika biznesowa aplikacji oparta jest o ADO.NET - przez lata zostały przygotowane tysiące zapytań SQL, które zostały zoptymalizowane oraz dokładnie przetestowane. Pojawiają się wymagania dotyczące utworzenia nowej wersji aplikacji gotowej na wymagania stawiane przez współczesny świat IT. Czy w takim przypadku ktoś zdecyduje się na użycie podejścia code-first? Niezbędna inwestycja setek roboczo-godzin, narażenie się na błędy oraz ryzyko niepowodzenia przy przepisywaniu całej logiki bizesowej. W takiej sytuacji zdecydujemy się na podejście database-first. Jest to również podejście, które praktykowałem przez ostatnie lata.

Z drugiej strony tworząc swoje własne aplikacje zawsze kieruje się podejściem code-first. Kontrola nad bazą danych odbywa się z poziomu kodu - możemy bardzo szybko reagować na zmiany, które pojawiają się wraz z rozwojem aplikacji. Zestaw narzędzi dostarczony przez EF Core jest wystarczający a sam proces migracji i aktualizowania schematu bazy danych szybki i sprawny (co nie oznacza, że bezproblemowy – zwłaszcza na początkowym etapie pracy).

EF Core vs EF 6

EF Core jest nową i ulepszoną wersja Entity Framework dla .NET Core. Musimy jednak pamiętać, że technologia ta jest ciągle rozwijana i nie jest tak dojrzała jak EF 6. Porównajmy zatem obie technologie.

Samego porównania dokonam w oparciu o EF Core 2.1 z uwagi na wsparcie długoterminowe oraz fakt, że w trakcie przygotowywania tego cyklu EF Core 5.0 istnieje w ‘wersji zapoznawczej 8’ – chce bazować na stabilnej i sprawdzonej wersji technologii.

EF Core wspiera i obsługuje funkcjonalności dobrze znane z EF6:

  • DbContext oraz DataSet;
  • Data Model (modelowanie);
  • Zapytania przy użyciu LINQ;
  • Śledzenie zmian (domyślnie włączone z możliwością zmiany przy użyciu AsNoTracking());
  • Zapisywanie zmian (SaveChanges());
  • Migracje (aktualizacje schematu bazy danych).

Zgodnie z zapowiedziami twórców EF Core będzie miał zaimplementowaną większość funkcjonalności dostępnych dla EF 6. Nie wszystkie z nich są wspierane przez EF Core 2.1:

  • Graficzna wizualizcja modelu (EDMX) – w przypadku migracji z EF6 najlepszą drogą (zalecaną) jest wygenerowanie nowego modelu na bazie istniejącej bazy danych;
  • Kreator modelu danych encji (Entity Data Model Wizard - funkcjonalność związana z podejściem database-first). Osoby korzystające z wcześniejszej wersji technologii będą pamiętały okno dialogowe pozwalające na utworzenie modelu na bazie wskazanego połączenia z bazą danych, wskazanie interesujących tabel, widoków oraz procedur a w efekcie utworzenie modelu *.edmx;
  • ObjectContext API - jest to relikt z czasów EF 1-4, kiedy platforma nie posiadała wsparcia obiektów POCO. EFCore wspiera DbContext oraz POCO;
  • Automatyczne migracje – twórcy EF Core na bazie swoich doświadczeń zdecydowali o nie wdrażaniu tej funkcjonalności tłumacząc, że ręczne stosowanie migracji zapewnia dużo łatwiejszą zarządzalność kodem;
  • Dziedziczenie: tabela według typu (TPT) - w tym podejściu wykorzystujemy osobną tabelę w bazie danych do przechowywania danych dla właściwości niedziedziczonych oraz kluczowych właściwości dla każdego typu w hierarchii dziedziczenia. Brak wsparcia w EF Core 2.1. Funkcjonalność została jednak udostępniona wraz z EF Core 5;
  • Dziedziczenie: Tabela według konkretnej klasy (TPC) - w tym podejściu używany dokładnie jednej tabeli dla każdej (nieabstrakcyjnej) klasy a wszystkie właściwości łącznie z właściwościami dziedziczonymi danej klasy mogą być mapowane do kolumn tabeli. Brak wsparcia w EF Core 2.1. Funkcjonalność została jednak udostępniona wraz z EF Core 5;
  • Wsparcie relacji wiele do wielu bez wykorzystania dodatkowej tabeli. Brak wsparcia w EF Core 2.1, rozwiązanie dostępne dla EF Core 5. Cała dyskusja dostępna tutaj: https://github.com/dotnet/efcore/issues/10508
  • Entity Splitting - funkcjonalność pozwalająca na podział naszej encji na wiele tabel bazodanowych. Dostępność od EF Core 3.0;

Na zakończenie sprawdźmy jakie nowe funkcjonalności są dostępne w EF Core, których nie mieliśmy w poprzedniej wersji technologii:

  • Wsparcie dla IoC (odwrócenie sterowania);
  • Unique constraints (wszystkie wartości w kolumnie są różne);
  • Alternatywne klucze - służą jako alternatywny, unikalny identyfikator dla każdej instancji jednostki oprócz klucza głównego: może zostać wykorzystany przy tworzeniu relacji pomiędzy tabelami;
  • Buforowanie DbContext - tworzenie puli wielokrotnego użytku jest przeciwieństwem tworzenia nowej instancji za każdym razem, gdy ta jest potrzebna. Takie podejście wpływa znacząco na wydajność pozwalając osiągnać wzrost około 20% w porównaniu z tradycyjnym podejściem. Taki wzrost wydajności otrzymujemy z uwagi na zmniejszony koszt połączeń wykonywanych do bazy danych;
  • Operacje wsadowe (batch) dla INSERT, UPDATE oraz DELETE;
  • Łatwa konfiguracja relacji przy wykorzystaniu Fluent API (o tym nieco później);
  • Możliwość testowania bazy danych w pamięci. Jest to niezwykle ciekawe zagadnienie dlatego po zakończeniu całego cyklu przygotuje również wpis dotyczący tej funkcjonalności;
  • Filtry zapytań globalnych – rozwiązanie takie ułatwia budowanie aplikacji opartych o architekturę multi-tenant. Zbudowany filtr jest automatycznie stosowany do wszystkich zapytań, które są wykonywanie w kontekście określonego typu. Framework dodaje taki filtr w klauzuli WHERE przed wykonaniem każdego zapytania. Popularnym miejscem zastosowania zapytań globalnych jest metoda OnModelCreating używana w obrębie danego kontekstu.

Podsumowanie

Kolejny raz krótki (z zamierzenia) wpis urósł do nieco dużych rozmiarów. Tak jak zawsze podkreślam – wiedza teoretyczna jest niezwykle istotna na drodze do poprawnego zrozumienia całej technologii i wykorzystania pełni jej możliwości. Mając podstawy będzie nam dużo łatwiej korzystać z wszelkich dostępnych funkcjonalności EF Core.