Code-first Migrations w Entity Framework, cz. 1
Migracje w Entity Framework są mechanizmem pozwalającym modyfikować strukturę istniejącej już bazy danych. Jednak funkcja ta jest dostępna jedynie przy podejściu Code-first, a więc wtedy kiedy najpierw napisaliśmy encje czyli klasy opisujące tabele, a dopiero na ich podstawie została stworzona baza. W dzisiejszym wpisie przedstawię podstawy korzystania z migracji, w przyszłości planuję opisać też bardziej skomplikowane przypadki dlatego w tytule znalazł się dopisek „cz. 1”. W przeciwieństwie do większości moich tutoriali i porad tutaj postaram się opisać wszystko w miarę dokładnie, nawet te bardziej podstawowe rzeczy.
W dalszej części będę korzystał z Entity Frameworka w wersji 6 oraz Visual Studio 2013, jednak dla wersji 2012 wszystko powinno wyglądać tak samo, w starszych nie mam pewności.
1. Projekt i przykładowe encje
Mimo, że Entity Framework jest zwykle głównie kojarzony z aplikacjami webowymi to tak na prawdę w żaden sposób obie rzeczy się nie łączą. Dlatego nie ma najmniejszych przeszkód przed stworzeniem dla kodu obsługującego bazę danych osobnego projektu w naszej solucji. Tak więc możemy spokojnie dodać projekt np. typu Class Library, który potem będzie można wykorzystać we właściwej aplikacji. Jednak tutaj utworzę całą aplikację webową żeby dodatkowo nie męczyć zagadnieniem rozdzielania funkcjonalności na kilka projektów, tak więc np. używana przeze mnie struktura katalogów będzie taka jak w webowej aplikacji ASP.NET MVC. Mój projekt nazywa się MigrationsApp.
Teraz można zacząć tworzyć encje. W tym tutorialu model bazy danych będzie bardzo prosty.
Dodaj do projektu w folderze Models publiczną klasę User z kilkoma właściwościami. Na jej podstawie pokażę działanie całego mechanizmu:
namespace MigrationsApp.Models { public class User { public int Id { get; set; } public string Login { get; set; } public string Password { get; set; } } }
Czas utworzyć DbContext. W tym celu dodaj do projektu publiczną klasę np. DbContext. i wypełnij ją jak poniżej, oczywiście dostosowując nazewnictwo do tego, którego Ty użyłeś. Ja stworzyłem tą klasę w folderze Models, czyli tam gdzie mam dodaną wcześniej encję.
using System.Data.Entity; namespace MigrationsApp.Models { public class DbContext : System.Data.Entity.DbContext { public DbSet<User> Users { get; set; } } }
Na koniec dodaj jeszcze connection string w pliku Web.config pamiętając, żeby nazwa connetion stringa była taka sama jak nazwa klasy tworzącej DbContext (czyli w naszym wypadku po prostu „DbContext”). Jest to potrzebne aby mechanizm migracji mógł połączyć się z bazą danych, dlatego też stworzyłem całą aplikację webową, ponieważ dzięki temu miałem plik Web.config od razu dostępny (w przypadku aplikacji desktopowej będzie to App.config).
W tym momencie masz już gotowy model, teraz przejdziemy do skorzystania z migracji.
2. Dodanie pierwszej migracji do projektu
Ważna informacja jest taka, że najlepiej migracje dodać jeszcze zanim wygenerujesz pierwsze tabele w bazie. Zmniejszy to ilość potencjalnych problemów w przyszłości i pozwoli na prostsze tworzenie nowej bazy np. przy przenoszeniu aplikacji na serwer.
Ok, uruchommy więc mechanizm migracji. Aby to zrobić wybierz z górnego menu TOOLS -> NuGet Package Manager -> Package Manager Console. Powinna Ci się pojawić (domyślnie w dolnej części) konsola:
Wpisz w niej „Enable-Migrations -ContextTypeName MigrationsApp.Models.DbContext” (gdzie „MigrationsApp.Models.DbContext” to pełny namespace twojego DbContextu) i zatwierdź klawiszem Enter. Zobacz, że po tej operacji w projekcie pojawił się nowy folder Migrations z jednym plikiem Configuration.cs. Od tej pory mechanizm migracji w twojej aplikacji jest już dostępny.
Teraz dobrze jest stworzyć pierwszą migrację, która będzie opisywała przejście z pustej bazy do naszej aktualnie napisanej struktury tabel. Dzięki temu możliwe będzie stworzenie bazy na nowo kiedy pojawią się kolejne migracje, inaczej dostawałbyś błąd o brakujących tabelach lub kolumnach ponieważ każda migracja opisuje jedynie zmianę względem poprzedniej wersji więc ta poprzednia wersja musi istnieć.
Aby dodać nową migrację wpisz w konsoli, którą przed chwilą używałeś „Add-Migration InitMigration” gdzie „InitMigration” to nazwa migracji, za każdym razem będziesz ją musiał wpisywać. Co prawda rzadko jest konieczność odwoływania się to starszych pozycji ale dobrze jest tutaj podawać sensowne nazwy. Dodatkowo taka moja osobista sugestia: nie podawaj jako nazwy migracji nazwy klasy, która istnieje (co możesz chcieć zrobić jak do bazy będziesz dodawał nową encję), zdecydowanie unikniesz kłopotów z podpowiadaniem składni ;)
Zostanie wygenerowany plik migracji, zobaczysz w nim dwie funkcje – Up() i Down(), będą one uruchamiane odpowiednio przy przechodzeniu „w górę”, a więc kiedy zmieniasz strukturę na nową, oraz przy przechodzeniu „w dół”, kiedy wracasz do starszej wersji bazy.
Właśnie dodałeś pierwszą migrację do projektu, czas na zaktualizowanie bazy danych, tak żeby zmiany zostały uwzględnione. Aby można było to wykonać musisz mieć w projekcie skonfigurowane połączenie z bazą danych (np. w postaci connection stringa w pliku Web.config) jednak tego zagadnienia nie będę omawiał bo skoro chcesz migrować dane w bazie to zakładam, że z samej bazy już korzystałeś.
W celu aktualizacji tabel wpisz w konsolę polecenie „Update-Database” i zatwierdź Enterem. Jeśli wszystko przebiegło pomyślnie powinieneś zobaczyć utworzone tabele w bazie danych. Po wykonaniu tego polecenia została utworzona baza (ponieważ korzystam z LocalDb, która może być tworzona właśnie w ten sposób przy pierwszym użyciu, w przypadku normalnego SQL Servera uważaj gdyż skrypt najpierw usunie bazę, a potem stworzy od nowa, a nie zawsze możesz mieć uprawnienia do tych operacji, zwłaszcza na zewnętrznych hostingach. Jak sobie z tym radzić i móc migrować bez usuwania danych i bazy przeczytasz w kolejnej części tego tutoriala), której struktura wygląda w moim przypadku tak:
Widać tutaj bazę MigrationsApp (taką nazwę podałem w connection stringu), która zawiera tabele Users utworzoną na podstawie dodanej wcześniej encji, a więc zawierającą kolumny Id, Login i Password oraz tabelę MigrationHistory przechowującą informacje o kolejnych migracjach.
3. Zmiana struktury bazy
Załóżmy teraz, że w trakcie pracy nad aplikacją okazało się, że użytkownik musi mieć też możliwość wpisania swojego imienia i nazwiska, w tym celu przydała by się nowa kolumna FullName typu string. Dopisz więc brakujące pole w klasie User, która teraz będzie wyglądała tak:
namespace MigrationsApp.Models { public class User { public int Id { get; set; } public string Login { get; set; } public string Password { get; set; } public string FullName { get; set; } } }
No dobra, ale jak teraz uaktualnić bazę? Kroki będą takie same jak przy InitMigration, a więc:
1. Wpisz w konsolę Add-Migration nazwa, i Enter
2. Wpisz w konsolę Update-Database, i Enter
I… koniec. Tyle wystarczyło, aby baza otrzymała nową strukturę:
4. Podsumowanie
Jak widać podstawy korzystania z bardzo pomocnego mechanizmu migracji są proste i nie powinny nikomu przysporzyć problemów, a pozwalają na dostosowywanie bazy danych do zmieniających się wymagań w trakcie powstawania projektu. Jedyna wada aktualnie pokazanego rozwiązania to za każdym razem tworzenie się bazy danych od nowa. Jednak rozwiązanie tego problemu istnieje i pokażę je w kolejnej części tutoriala. Kiedy będziesz potrafił korzystać z migracji mając bazę wypełnioną danymi na pewno zobaczysz jak wygodnym i ułatwiającym pracę mechanizmem są.
Witam! Świetny wpis, a kiedy druga część?
Miło mi czytać, że się podoba :) Jeśli chodzi o drugą część to była ona już dawno planowana jednak wraz ze zmianą typu zamieszczanych treści nie została jeszcze napisana. Jednak jeśli jest zainteresowanie to oczywiście z chęcią powrócę o tematu migracji w Entity Frameworku dlatego zachęcam do śledzenia bloga na bieżąco ponieważ trudno mi określić dokładny termin kiedy mogła by się pojawić część druga.
Prosto i na temat. Życzę dalszych treściwych i jasnych publikacji.