Migracja schematu bazy danych z Fluent Migrator

Wprowadzenie

W ubiegłym tygodniu pokazałem Ci w jaki sposób migrować schemat bazy w Entity Framework (zachęcam pierw do przeczytania tamtego wpisu). W tym wpisie pokaże Ci inną bibliotekę, którą możesz użyć do migracji schematu bazy, w momencie, gdy z jakiegoś powodu nie możesz użyć Entity Framework. Biblioteka nazywa się Fluent Migrator (https://fluentmigrator.github.io/) i w swoim działaniu jest bardzo podobna do mechanizmu migracji z Entity Framework. Największą różnicą miedzy tymi narzędziami jest to, że w Fluent Migrator musimy sami napisać całą migrację. A nie tak jak w Entity Framework, gdzie migracja jest generowana na podstawie zmian w modelu.

Fluent Migrator

Migracje w Fluent Migrator są zorganizowane w podobny sposób jak w Entity Framework. Czyli dodajemy nową klasy dla każdej migracji. Klasa migracji musi dziedziczyć po klasie Migration i implementować dwie metody: Up oraz Down. Pierwsza metoda podnosi schemat bazy danych. Druga natomiast służy do cofania zmian.

Fluent Migrator udostępnia wiele metod, za pomocą których możemy definiować schemat bazy danych. Nie musimy wszystkiego pisać w sql i korzystamy z dostarczonego fluent api (stąd nazwa biblioteki). Plusem takiego podejścia jest to, że Fluent Migrator generuje odpowiedniego sql dla każdego silnika baz danych jaki wspiera. Jak zobaczysz później trochę tego jest.

Migracja

Najlepiej od razu zobaczyć pierwszą migrację w akcji. Będzie to ten sam przykład co w poprzednim wpisie. Poniżej migracja, która dodaje tabele Products z trzema kolumnami

Na pierwszy rzut oka widać, że powyższa migracja jest podobna do migracji wygenerowanych przez Entity Framework. Również korzystamy z dostępnych metod, za pomocą których określany zmiany w strukturze bazy danych. Na podstawie tych metod, później Fluent Migrator wygeneruje odpowiedniego SQL, który zostanie wykonany na bazie danych.

W migracji warto zwrócić uwagę na dwie rzeczy. Po pierwsze widać, że klasa dziedziczy po klasie Migration i nadpisuje dwie metody Up oraz Down.

Dodatkowo zobacz, że migracja jest udekorowana atrybutem Migration z dziwnym numerem. Numer ten określa wersję migracji. W przypadku Fluent Migrator jest liczba typu long. Biblioteka na podstawie tej liczby określa później między innymi kolejność wykonywania migracji. Osobiście w określeniu wersji migracji stosuje datę dodanie migracji – rok-miesiąc-dzień-godzina-minuta (oczywiście bez myślnika). Dzięki czemu dużo łatwiej pracuje się na migracjach w różnych branch niż, gdybym stosował kolejne liczby naturalne. Z drugiej strony przedział minutowy jest na tyle mały, że szansa dodanie dwóch migracji z tą samą wersję przez dwie różne osoby jest bardzo mała.

Wersję migracji dodaje również do nazwy plików z migracjami, podobnie jak to robi Entity Framework. Dzięki temu łatwiej później widać kolejność migracji w Solution Explorer:

fluent migrator solution explorer

Uruchomienie migracji

Podobnie jak w przypadku migracji w Entity Framework, również w przypadku Fluent Migrator do uruchamiania migracji wykorzystuje dedykowaną aplikacje konsolową. W tym przypadku również klasy migracji znajdują się w projekcie migratora (co widać na zrzucie ekranu powyżej).

Aby utworzyć migratora należy w pierwszej kolejności zainstalować pakiet FluentMigrator.Runner. Zainstaluje on wszystkie inne niezbędne pakiety. Pakiet ten ma również jeden minus. Instaluje on runnery dla wszystkich wspieranych baz danych przez Fluent Migrator. Ale z racji, że jest to oddzielna aplikacja nie jest to duży problem dla mnie.

Testowy projekt korzysta z CommandLineParser do parsowanie parametrów aplikacji oraz nloga do obsług logów.

Tym razem obiekt parametrów rozszerzyłem o dodatkowy parametr (v – version), który umożliwia przekazanie numery wersji do jakiej trzeba cofnąć schemat bazy danych:

Natomiast sama klasa Program z uruchomieniem migracji wygląda tak:

Metoda CreateServices jest odpowiedzialna za skonfigurowanie runnera migracji. W niej określamy między innymi dla jakiego silnika mają zostać wygenerowane sql. Jakiego użyć connection stringa, czy gdzie znajdują się migracje.

Natomiast metoda UpdateDatabase wykonuje aktualizacje lub cofnięcie schematu bazy danych. W zależności od parametru version przekazanego do aplikacji.

Jak widać kod nie jest jakoś bardzo mocno skomplikowany. Można go rozbudować o jakieś dodatkowe funkcjonalności, które potrzebowalibyśmy w systemie. Na przykład dodawanie testowych danych.

Przykłady uruchomienia aplikacji z poziomu wiersza poleceń:

Druga migracja

Jak widać na zrzucie ekranu powyżej testowy przykład posiada dwie migracje. Podobnie jak w poprzednim wpisie druga migracja jest odpowiedzialna za utworzenie dedykowanej tabeli dla kategorii, przekopiowanie danych z kolumny Category z tabeli Products oraz ustawienie id kategorii (kolumna CategoryId) w tabeli z produktami. Sama migracja wygląda tak:

Dalej większość kodu migracji to korzystanie z dostępnego API. Jedynie samo skopiowanie danych jest zrealizowane przez użycie czystego SQL.

Wygenerowany SQL

Warto przejrzeć jeszcze jaki sql zostanie wygenerowany przez Fluent Migratora. Tutaj wspomne o kolejnej różnicy między Entity Framework a Fluent Migratorem. Entity Framework jest w stanie nam utworzyć bazę danych w przypadku, gdy jej nie ma. Natomiast Fluent Migratora zakłada, że jest już ona utworzona. Dlatego przed uruchomieniem testowej aplikacji upewnij się, czy testowa baza istnieje.

Na listingu poniżej znajduje się log z działania aplikacji, która została uruchomiona na pustej (dopiero co utworzonej) bazie danych:

Biblioteka w pierwszych kilku zapytaniach sql tworzy specjalną tabelę (VersionInfo), do której później zapisuje informacje o wykonanych migracjach. Dzięki temu wie jakie migracje zostały wykonane na bazie i jakie jeszcze trzeba wykonać.

Kolejne zapytania są już właściwymi zapytaniami z migracji. Polecam przejrzeć wykonane zapytania i porównać to z tym jakie samemu by się je napisało.

Entity Framework vs Fluent Migrator

Na koniec warto zastanowić się, z której biblioteki skorzystać. Niewątpliwą zaletą Entity Framework jest to, że sama biblioteka generuje większość kodu migracji na podstawie zmian modelu. My jako programiści musimy jedynie dodać pojedyncze sql, które są odpowiedzialne za skopiowanie danych. W przypadku Fluent Migratora niestety musimy wszystko napisać samemu.

Dlatego w moim przypadku w pierwszej kolejności staram się wybrać migracje w Entity Framework, gdy mam taką możliwość. Gdy z jakiś powodów nie mogę w projekcie skorzystać z Entity Framework (np. jest użyty innym ORM np. NHibernate) wtedy korzystam z Fluent Migratora.

Przykład

Na githubie (https://github.com/danielplawgo/FluentMigratorExample) znajduje się przykład do tego wpisu. Praktycznie cały kod znajduje się w projekcie FluentMigratorExample.Migrator, który należy uruchomić. W właściwościach projektu w zakładce Debug znajduje się connection string, który domyślnie wykorzystuje aplikacji, gdy jest uruchomiona z poziomu Visual Studio. Aby aplikacja się uruchomiła poprawnie należy upewnić się, że baza określona przez connection string istnieje lub zmienić go na właściwą bazę danych.

Gorąco zachęcam do pobrania przykładu i sprawdzenie Fluent Migratora w praktyce.

Podsumowanie

W tym wpisie pokazałem Ci inną bibliotekę, którą możesz wykorzystać do tworzenie migracji. Fluent Migrator, o którym mowa, jest bardzo podobny w swym działaniu do mechanizmu migracji w Entity Framework. Dlatego jest to mój pierwszy wybór w momencie, gdy nie mogę skorzystać z Entity Framework w swoim projekcie.

Za tydzień kolejna biblioteka (DbUp), która tym razem będzie działać trochę inaczej. Przydaje się ona, gdy migracje mamy zapisane w wielu plikach sql. Ale o tym już w kolejnym wpisie.

1 thought on “Migracja schematu bazy danych z Fluent Migrator

Dodaj komentarz

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