I arbejdet med databaser og datalagring i QuestGame undersøger jeg i øjeblikket, hvad det betyder for databasen, at vi arbejder med Entity Framework Core og Code First.
Hvor jeg fra backendperspektivet primært er interesseret i entities, DbContext, dataadgang og migrations, er mit fokus her et andet: Hvordan bliver backendens modeller omsat til et konkret databaseskema, og giver det resulterende design mening?
Code First betyder nemlig ikke, at databasedesignet forsvinder. Det betyder snarere, at en stor del af designet bliver beskrevet gennem C# og EF Core-konfiguration frem for direkte gennem SQL.
Fra entities til tabeller
I QuestGame arbejder vi blandt andet med QuestItem, som kan være en Quiz, Activity eller Task. En Quiz har desuden fire AnswerOption.
En relation kan eksempelvis beskrives i EF Core:
modelBuilder.Entity<Quiz>()
.HasMany(q => q.AnswerOptions)
.WithOne(a => a.Quiz)
.HasForeignKey(a => a.QuizId);Code language: C# (cs)
Fra et databaseperspektiv er det interessante ikke kun selve C#-koden, men hvad den betyder for databaseskemaet.
EF Core kan eksempelvis omsætte relationen til en fremmednøgle mellem de relevante tabeller. Derfor forsøger jeg at se modellerne fra begge sider:

Spørgsmålet bliver dermed ikke kun, om relationen fungerer i backend, men også om kardinalitet, fremmednøgler og constraints svarer til den struktur, vi faktisk ønsker i databasen.
Migrations viser det konkrete resultat
Når modellen ændres, kan jeg oprette en migration:
dotnet ef migrations add AddQuizAnswerOptionsCode language: Bash (bash)
Migrationen er interessant i databasearbejdet, fordi den viser, hvordan EF Core konkret forsøger at ændre databaseskemaet.
En del af en migration kunne eksempelvis indeholde:
migrationBuilder.AddForeignKey(
name: "FK_AnswerOptions_QuestItems_QuizId",
table: "AnswerOptions",
column: "QuizId",
principalTable: "QuestItems",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);Code language: C# (cs)
Her bliver nogle af de databasebeslutninger, der ellers ligger skjult bag modellen, tydelige. Der oprettes blandt andet en fremmednøgle, og der træffes samtidig et valg om, hvad der skal ske med tilhørende data ved sletning.
Jeg ser derfor migrations som mere end blot en metode til at opdatere databasen. De kan også bruges til at undersøge og kontrollere, hvad vores Code First-model faktisk medfører.
Constraints og dataintegritet
Et område jeg også undersøger, er hvilke regler der bør håndhæves direkte i databasen.
Hvis en egenskab eksempelvis er obligatorisk, kan den konfigureres gennem EF Core:
modelBuilder.Entity<AnswerOption>()
.Property(a => a.Text)
.IsRequired()
.HasMaxLength(500);Code language: C# (cs)
Det kan blandt andet få betydning for, om kolonnen tillader NULL, og hvor meget data der kan gemmes.
Her bliver grænsen mellem backendvalidering og dataintegritet relevant. Backend kan kontrollere input, men nogle regler kan muligvis med fordel også håndhæves gennem databaseskemaet, så ugyldige data ikke kan gemmes, uanset hvor de kommer fra.
Indeks og forespørgsler
Code First kan også bruges til at definere indeks:
modelBuilder.Entity<AnswerOption>()
.HasIndex(a => a.QuizId);Code language: C# (cs)
Jeg vil dog ikke betragte et indeks som noget, der nødvendigvis bør oprettes, bare fordi det er muligt. I stedet vil jeg undersøge, hvilke forespørgsler systemet faktisk foretager, og om bestemte indeks kan være relevante for dem.
Det bliver særligt interessant i QuestGame, fordi vi også arbejder med lokationsdata. Her kan valget af database og dens understøttelse af geografiske data få betydning for både datatyper, indeks og hvilke forespørgsler der med fordel kan udføres direkte i databasen.
Er det genererede skema hensigtsmæssigt?
En af de vigtigste ting, jeg undersøger, er derfor forskellen mellem:
“EF Core kan generere databasen”
og
“EF Core har genereret et godt databaseskema.”
De to ting er ikke nødvendigvis det samme.
Jeg vil blandt andet være opmærksom på:
- hvordan entities bliver fordelt på tabeller
- hvilke primær- og fremmednøgler der oprettes
- hvilke constraints der håndhæves
- hvilke datatyper EF Core vælger
- hvor indeks kan være relevante
- hvordan arv mellem
QuestItem,Quiz,ActivityogTaskrepræsenteres - om data bør lagres eller beregnes
- om databasen understøtter de forespørgsler, projektet har behov for
Mit arbejde med Code First fra databaseperspektivet handler derfor ikke kun om at generere migrations og køre database update. Jeg forsøger i højere grad at forstå hvordan valg i backendmodellen påvirker databaseskemaet, og om det resultat, EF Core producerer, passer til projektets behov for struktur, integritet og dataadgang.
