Migracja schematu bazy danych z DbUp

Wprowadzenie

Dwa ostatnie wpisy na blogu dotyczyły sposobu migracji schematu bazy danych. Migracje w Entity Framework oraz Fluent Migrator charakteryzują się tym, że cały proces migracji jest zapisany w klasach za pomocą api, które udostępnia dana biblioteka. Ilość sqla, którą piszemy w tych migracjach, jest minimalizowana i sprowadza się głównie do wykonywania jakiś specyficznych rzeczy – np. skopiowania danych z jednego miejsca w drugie. Natomiast dzisiejsza biblioteka (DbUp – https://dbup.github.io/) działa zupełnie inaczej. Pomaga ona wykonywać migracje, które są zapisane w plikach sql. Dlatego właśnie DbUp jest moim pierwszym wyborem w sytuacji, gdy w projekcie migracje są zdefiniowane w skryptach.

DbUp

Podobnie jak w przypadku Entity Framework oraz Fluent Migrator, uruchamianie migracji organizuję w dedykowaną aplikację konsolową. Dlatego w przykładzie na githubie (https://github.com/danielplawgo/DbUpExample) znajduje się tylko aplikacja konsolowa. Aby skorzystać z DbUp, należy zainstalować pakiet https://www.nuget.org/packages/dbup z nugeta. W przykładzie dodałem również pakiety dla CommandLineParsera oraz NLoga, aby móc przekazać do aplikacji connection stringa oraz logować działanie migratora – podobnie jak to było w poprzednich przykładach.

Jak wspomniałem wyżej, wszystkie migracje będą zapisane w plikach sql. Tworzę dla nich w projekcie dedykowany katalog. Również tutaj nazwy plików migracji zaczynają się od daty dodania migracji – aby później można było łatwiej analizować to, co dzieje się z schematem bazy na przestrzeni czasu:

dbup solution explorer

Domyślnie DbUp wymaga, aby pliki sql z migracjami miały ustawiony Build Action na Embedded Resource w ustawieniach pliku:

dbup file properties

Jest to również jeden z częstych problemów z nieuruchamiającymi się migracjami w DbUp. Domyślnie nowy plik sql ma ustawiony Build Action na Content i trzeba pamiętać, aby zmienić to ręcznie.

Pierwsza migracja

W testowym projekcie posłużymy się tym samym przykładem, co w poprzednich dwóch wpisach. Dzięki temu będziesz mógł porównać sposoby działania wszystkich trzech bibliotek. Dlatego w przykładzie pierwsza migracja dodaje do bazy danych tabelę Products z trzema kolumnami oraz kilka rekordów:

Jak widać, w tym przypadku całą zmianę schematu bazy danych mamy zapisaną w czystym sqlu. W C# zapiszemy jedynie sposób uruchamiania aplikacji, natomiast wszystko inne będzie w sqlu.

Uruchomienie migracji

Samo uruchomienie migracji nie jest mocno skomplikowane, jest to kilkanaście linii kodu. Podobnie jak inne biblioteki tego typu, DbUp korzysta z specjalnej tabeli, w której zapisuje informacje o wykonanych skryptach. Podczas uruchamiania sprawdza, jakie migracje są dostępne w aplikacji, i uruchamia tylko te, które nie zostały wcześniej uruchomione.

Warto pamiętać o tym, że DbUp rozpoznaje skrypty po ścieżce w projekcie oraz nazwie pliku. Na przykład pierwsza migracja będzie rozpoznana jako „DbUpExample.Migrator.Migrations.201810100538_AddProducts.sql”. Przez to musimy uważać z przenoszeniem migracji do innego folderu, ponieważ DbUp może rozpoznać to jako zupełnie inną migrację.

Tak jak w poprzednich przykładach z migracjami, tak i w tym wykorzystałem klasę Options, która służy do parsowania parametrów za pomocą biblioteki CommandLineParser przekazywanych do aplikacji. Klasa Options zawiera tylko jeden parametr dla connection stringa:

Natomiast kod potrzebny do uruchomienia migracji wygląda tak:

Jak widać, po sparsowaniu parametrów z wiersza poleceń w metodzie Migrate konfigurowany jest migrator z DbUp. Określamy w nim przede wszystkich connection stringa do bazy, którą chcemy zaktualizować. Do tego korzystamy z dostępnych providerów (wywołanie metody WithScriptsEmbeddedInAssembly), którzy określają, skąd mają zostać załadowane pliki sql. W przykładzie są to dołączone pliki sql do projektu. DbUp udostępnia również inne sposoby ładowania plików – https://dbup.readthedocs.io/en/latest/more-info/script-providers/

DbUp wpiera też różne biblioteki do logowania działania aplikacji, takie jak nlog, log4net. Konfiguruje się je za pomocą metody LogToAutodetectedLog, która pod spodem korzysta z LibLog – https://github.com/damianh/LibLog

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?

Druga migracja

Druga migracja, którą dodałem do projektu, jest odpowiedzialna za utworzenie tabeli Categories, skopiowanie do niej nazwy kategorii oraz ustawienie CategoryId w tabeli Products. Sam sql migracji wygląda tak:

Wynik działania migratora

Poniżej znajduje się wynik działania migratora, który został uruchomiony dla pustej, dopiero co utworzonej bazy danych. Widać, że migrator wykrył, iż żadna migracja nie została wykonana, i wykonał obie migracje, które są dodane do aplikacji.

Filtrowanie migracji

Na koniec warto jeszcze wspomnieć o ciekawej funkcjonalności, którą daje DbUp i której używałem w przykładzie. DbUp, szukając migracji do wykonania, umożliwia również filtrowanie znalezionych migracji. Robi się to poprzez przekazanie metody filtrującej (przyjmującej stringa z pełną nazwą migracji i zwracającą true lub false) do metody WithScriptsEmbeddedInAssembly, jak to zrobiłem w powyższym przykładzie.

Dzięki temu możemy używać jednej aplikacji do aktualizowania schematu różnych baz danych. Wystarczy na przykład dla każdego typu bazy danych utworzyć dedykowany katalog i do niego wrzucać skrypty. Później w metodzie filtrującej możemy na podstawie jakiegoś parametru przekazanego do aplikacji decydować, z którego katalogu będą się ładować skrypty. Tak jak w przykładzie – korzystając z metody StartWith klasy string.

Innym przykładem użycia takiego podejścia może być dodanie folderów z różnymi danymi testowymi, które również będzie filtrować metoda na podstawie parametrów przekazanych do aplikacji. Filtrowanie działa również w przypadku innych providerów.

Przykład

Tradycyjnie na githubie (https://github.com/danielplawgo/DbUpExample) znajduje się przykład, którego użyłem podczas przygotowywania tego wpisu. Podobnie jak w przypadku pozostałych przykładów z migracjami schematu bazy danych, tak i w tym, aby uruchomić przykład, musisz w ustawieniach projektu ustawić poprawny connection string do bazy, na której mają wykonać się migracje.

Zachęcam do pobawienia się przykładowym projektem.

Podsumowanie

DbUp jest kolejną biblioteką, której możesz użyć do zarządzania schematem baz danych. Jak widać na przykładzie, biblioteka przydaje się głównie wtedy, gdy planujemy w projekcie używać migracji zapisanych w czystym sqlu. Dlatego w pierwszej kolejności to ją proponuję zespołom, które używają plików sql do zmiany schematu bazy danych, a nie korzystają jeszcze z żadnego narzędzia do automatyzowania uruchamia migracji i wszystko robią ręcznie.

Migracje z Entity Framework lub Fluent Migratora używam w sytuacji, gdy decydujemy się na tworzenie migracji w kodzie za pomocą dostępnego api, a nie w czystym sqlu.

Używałeś DbUp? Jakie są Twoje doświadczenia z tą biblioteką? A może używasz innego narzędzia, które ma podobne funkcje jak DbUp? Proszę, podziel się tym w komentarzu. 🙂

1 thought on “Migracja schematu bazy danych z DbUp

Dodaj komentarz

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