Entity Framework w osobnym projekcie
Jeśli szukasz szybkiego rozwiązania, kliknij tu. Jeśli chcesz się nieco więcej dowiedzieć, przeczytaj cały post.
Wstęp
Gdy tworzymy nową aplikację z identyfikacją użytkowników (Identity) w VisualStudio, domyślny kreator tworzy jeden projekt, do którego pcha wszystkie klasy. Do malutkich rzeczy, czy nauki to w zupełności wystarczy. Jednak w świecie rzeczywistym chcielibyśmy mieć osobny projekt do modeli i osobny projekt dla warstwy danych (Data Access Layer).
Niby nie jest to trudne, wystarczy przenieść nasz DbContext
do innego projektu i już. A co z migracjami? Migracje nadal będą się tworzyć w projekcie głównym. Nie o to chodzi. Chcemy migracje też w projekcie z danymi.
Dlaczego to nie jest oczywiste?
Musisz zdać sobie sprawę z tego, jak działają migracje w Entity Framework (czy też EfCore), a także jak działa aktualizacja bazy danych.
Gdy uruchamiasz polecenie Add-Migration
lub dotnet ef migrations add
, narzędzie uruchamia Twoją główną aplikację. Uruchomienie aplikacji następuje w sposób normalny. Czyli przy aplikacji konsolowej, odpalona zostanie metoda Main. Przy aplikacji webowej, pójdzie cała konfiguracja.
Jednym z kroków jest inicjalizacja Entity Framework, np:
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")));
W tym momencie tworzymy połączenie z bazą danych i migracje mogą zostać utworzone. Pamiętaj, że do utworzenia migracji konieczne jest połączenie z bazą danych. Narzędzie musi sprawdzić, jak wygląda baza i jak wygląda model – musi mieć możliwość porównania tego.
Teraz jeśli uruchomisz migrację z parametrem -p
, wskazując na konkretny projekt, np:
Add-Migration InitialDbCreate -p DataAccessLayer
Entity Framework będzie próbowało uruchomić projekt DataAccessLayer. Jeśli jest to zwykła biblioteka klas (class library), no to co się uruchomi? Nic. Dlatego też migracja nie będzie mogła się odbyć.
Ale można to nieco obejść. Narzędzie poszuka jeszcze klasy, która implementuje pewien interfejs. Jeśli znajdzie taką, utworzy jej obiekt i za jej pomocą skonfiguruje połączenie z bazą danych.
Rozwiązanie
- W swoim projekcie z danymi (tam, gdzie masz
DbContext
i chcesz mieć migracje) musisz utworzyć klasę implementującą specjalny interfejsIDesignTimeDbContextFactory
. Ef właśnie tego poszuka (jeśli używasz Sql Servera, dodaj pakiet nuget:Microsoft.EntityFrameworkCore.SqlServer
):
public class DbContextFactory : IDesignTimeDbContextFactory<ApplicationDbContext>
{
public XMoneyDbContext CreateDbContext(string[] args)
{
DbContextOptionsBuilder<ApplicationDbContext> optionsBuilder = new DbContextOptionsBuilder<ApplicationDbContext>();
optionsBuilder.UseSqlServer("tutaj Twój connection string")
return new ApplicationDbContext(optionsBuilder.Options);
}
}
Przeanalizujmy go:
- deklarujesz fabrykę kontekstu bazy danych (Ef poszuka właśnie klasy implementującej ten interfejs), parametrem generycznym jest oczywiście Twój kontekst bazy danych.
- najpierw tworzysz buildera do opcji kontekstu
- ustawiasz opcje (np. UseSqlServer) i connection string
- tworzysz swój kontekst i zwracasz go
I to właściwie tyle. Możesz już teraz uruchomić migrację z przełącznikiem -p:
Add-Migration NazwaMigracji -p NazwaTwojegoProjektu
lub
dotnet ef migrations add NazwaMigracji -p NazwaTwojegoProjektu