WORDS
Entity Framework med Code first
För att arbeta med databaser i C# använder man ofta Entity Framework (EF). Det är ett ORM-system (objekt-relationell mappning), som låter en skriva kod som om det vore vanliga objekt, istället för att manuellt skriva SQL-kod, exekvera den och därefter konvertera resultatet till objekt.
Här följer ett exempel på hur det kan se ut med och utan ett ORM.
// Manuell SQL-kod
string sql = "SELECT id, name FROM user WHERE id = 10";
QueryResult res = db.ExecuteQuery(sql);
string name = (string)res.First()["name"];
// Med ett ORM-system
User user = dbContext.Users.First(x => x.Id == 10);
string name = user.Name;
Det finns flera olika ORM-ramverk för C#, men Entity Framework är det populäraste. För andra programmeringsspråk andra ramverk, men jag har aldrig stött på något som känts lika smidigt som Entity Framework.
Entity Framework
Entity Framework är ett ORM-system för C# som fungerar särskilt bra med Microsofts SQL Server. Det finns tre, kanske fyra, huvudsakliga sätt som man använder Entity Framework: med utgångspunkt i C#-koden, i databasen eller i ett modellspråk - och kanske också utan att utgå från något över huvud taget.
- "Code first". Utifrån C#-klasser (entiteter) skapar man mer eller mindre automatiskt migreringsfiler. Med dessa kan man sedan helautomatiskt skapa och underhålla databastabeller.
- "Database first". Utifrån dom tabeller och kolumner som faktiskt finns i en databas genererar man C#-klasser.
- "Model first". Utifrån ett särskilt datamodellspråk genererar man både tabeller i databasen och C#-klasser.
- Namnlös. Man håller C#-klasserna och databasen uppdaterade manuellt.
Det här inlägget handlar om "code first". Det är inte en tutorial eller dokumentation. Om du är intresserad av det, kika t.ex. på Microsofts introduktion till EF och deras artikel om "code first". Jag tänker istället översiktligt beskriva hur man går tillväga för att göra en ändring och migrering, och några av fördelarna och fallgroparna.
Göra en ändring
Varje gång jag ska göra en ändring i tabellstrukturen följer jag denna mall. Inte alla punkter behöver göras varje gång, och ibland måste man göra samma punkt flera gånger.
Skapa/uppdatera entiteterna. Lägg till nya C#-klasser, lägg till nya properties eller ta bort gamla etc.
Lägg till/ta bort entiteter från DbContext. Om jag har lagt till en helt ny klass eller tagit bort den, så behöver också min DbContext-klass uppdateras.
Gör index- eller andra konfigurationsändringar. Jag föredrar att konfigurera index, foreign keys och särskilda kolumninställningar i en särskild konfigurationsklass. Nu är det dags att göra dessa ändringar.
Skapa automatisk migrering. Öppna "Package manager console" i Visual Studio, och kör detta kommando:
EntityFramework\Add-Migration NamnPåMigreringen -ProjectName Namn.På.Ditt.Visualstudioprojekt -Force
Tre filer kommer automatiskt att skapas, men dom visas som en enda filgrupp i Visual Studio.
Granska migreringen. Granska dom automatiskt skapade filerna. Följde mina ändringar med? Om jag döpte om något, så kommer Entity Framework troligen att ha skapat en migrering där tabellen/kolumnen raderas och sedan återskapas. Gör om det till en omdöpning, för att inte riskera att förlora data.
Ta bort
Down()
-metoden. Som standard, skapar Entity Framework både en migrering för att göra mina ändringar och för att ångra dom. Om jag i min migrering lägger till en tabell, så kommerDown()
-metoden att ta bort den. Jag kommer säkert inte att orsaka att ner-migreringen körs, men för att eliminera all risk, radera metoden helt!Generera och granska SQL:en. Entity Frameworks migrerings-api kan vara svårtytt ibland. Därför genererar jag SQL:en som migreringen motsvarar och granskar den. Det gör man genom att köra följande kommando i "Package manager console":
EntityFramework\Update-Database -ProjectName Namn.På.Ditt.Visualstudioprojekt -v -Script
Eftersom jag har med
-Script
, kommer SQL:en att genereras och visas, istället för att faktiskt uppdatera databasen.Kör SQL:en manuellt. Om jag är det minsta osäker på om SQL:en gör det som jag vill att den gör, så kopierar jag den och kör den SQL Server Management Studio. Och förstås committar jag inga ändringar:
BEGIN TRANSACTION -- Kopiera den genererade SQL-koden ROLLBACK
Detta kan t.ex. visa om jag försöker skapa ett unikt index på en kolumn som inte innehåller unika värden.
Kör applikationen lokalt. Nu är det äntligen dags att testa applikationen lokalt!
Committa och pusha. Allt ser bra ut, så nu är det bara att committa och pusha koden. Förhoppningsvis raderar jag inte hela databasen
Fördelar
Entity Framework med "code first" är mitt favoritsätt att arbeta med databaser från C#.
- Lätt att komma igång, för man "behöver ingen SQL-kunskap" (men det kan gå illa om man inte har det!)
- Strukturerat och tydligt sätt att hantera migreringar på
- Kan jobba direkt i C#
- Namngivning på kolumner, nycklar och index blir mer konsekventa
- Lätt att komma ihåg foreign keys, eftersom det i dom enklare fallen kan läggas till automatiskt givet en "navigation property"
Fallgropar
Det finns dessvärre många fallgropar som man måste känna till om man vill använda "code first".
Merge. Vad händer om du gör en migrering i din Git-gren, och en kollega gör en i en annan? Hur får man till en merge? Det gäller att hålla tungan rätt i mun, för alla migreringar genererar en lång binärsträng som representerar hela din datamodell. För att åstadkomma en korrekt merge, måste du i merge-committen skapa en ny, tom merge-migrering:
EntityFramework\Add-Migration MergeFix -ProjectName Namn.På.Ditt.Visualstudioprojekt -Force
Där kan du ta bort både Up()
- och Down()
-metoden. Obs: glöm inte att verifiera i databasen, att tabell- och kolumnförändringarna ser bra ut!
Namnbyte. När man döper om en klass eller en property, så kommer Entity Frameworks migreringsverktyg att anta att du ville radera en tabell/kolumn och skapa en annan. Då ändrar man migreringen, så att det istället blir en rename. Men, vad händer då med eventuella index som innehåller den kolumnen? Dom döps normalt inte om automatiskt. Det enklaste kan ofta vara att radera indexet och återskapa det, för det finns inget inbyggt sätt att döpa om index.
Det är möjligt att detta fungerar bättre i Entity Framework Core än i Entity Framework 6 - det har jag inte testat.
När ska man undvika "code first"?
Man kan inte använda "code first" i alla projekt. Det fungerar bara när C#-projektet styr databasen och får bestämma över vad som finns och inte finns där. Om något annat system har kontroll över databasen, så är det bättre att använda "database first".
"Code first" innebär inte att man slipper vara expert på databaser. Det kräver faktiskt nästan mer kunskap. Man måste ju både förstå dom intrikata detaljerna i Entity Framework, dess migrerings-api och SQL. "Code first" gör det också skrämmande lätt att radera tabeller och kolumner av misstag, så det gäller att hålla tungan rätt i mun. Om projektet har många juniora utvecklare som behöver göra databasförändringar, så kanske det är bättre att faktiskt skriva SQL-kod.
"Code first" är ytterligare ett lager mellan C#-koden och databasen, så det gör hela lösningen mer komplex. Om man vill att det ska vara lätt att förstå hur lösningen fungerar under ytan, så kanske man vill undvika "code first".
Slutord
När du har använt Entity Framework ett tag, så kanske du blir intresserad av hur man med minimal kod kan göra alla databasmigreringar. Då är det dags för "code first".