I arbejdet med backend til QuestGame undersøger jeg i øjeblikket, hvordan Entity Framework Core Code First kan bruges til at opbygge og vedligeholde datamodellen.

Med Code First beskrives datamodellen først gennem C#-klasser og konfiguration i backendprojektet. EF Core kan derefter bruge modellen som grundlag for databaseskemaet og håndtere ændringer gennem migrations.
Det gør tilgangen interessant i min rolle som backendudvikler, fordi backendens modeller og databasen bliver tæt forbundet. Samtidig giver det anledning til at undersøge, hvor grænsen går mellem backendudvikling og databasedesign.
Datamodellen beskrives i backend
I QuestGame arbejder vi blandt andet med Quest og QuestItem, hvor et QuestItem kan være en Quiz, Activity eller Task. En Quiz indeholder desuden fire AnswerOption.
En forenklet model kunne eksempelvis se sådan ud:
public abstract class QuestItem
{
public Guid Id { get; set; }
public string Title { get; set; } = string.Empty;
}
public class Quiz : QuestItem
{
public List<AnswerOption> AnswerOptions { get; set; } = [];
}
public class Activity : QuestItem
{
}
public class Task : QuestItem
{
}
public class AnswerOption
{
public Guid Id { get; set; }
public string Text { get; set; } = string.Empty;
public Guid QuizId { get; set; }
public Quiz Quiz { get; set; } = null!;
}Code language: C# (cs)
Det interessante ved Code First er, at disse klasser ikke kun bruges af backendens kode. De er samtidig med til at danne grundlag for, hvordan data bliver repræsenteret i databasen.
Jeg undersøger derfor blandt andet, hvordan modellerne bør struktureres, så de giver mening i domænet, samtidig med at EF Core kan omsætte dem til et hensigtsmæssigt databaseskema.
DbContext forbinder modellen med EF Core
For at EF Core kan arbejde med modellerne, registreres de gennem en DbContext.
Et forenklet eksempel kunne være:
public class AppDbContext : DbContext
{
public DbSet<Quest> Quests => Set<Quest>();
public DbSet<QuestItem> QuestItems => Set<QuestItem>();
public DbSet<AnswerOption> AnswerOptions => Set<AnswerOption>();
public AppDbContext(DbContextOptions<AppDbContext> options)
: base(options)
{
}
}Code language: C# (cs)
Her fungerer AppDbContext som forbindelsen mellem backendens objektmodel og databasen.
EF Core kan ofte udlede relationer gennem konventioner, men jeg undersøger også, hvornår det giver mening at konfigurere dem eksplicit.
Eksempelvis kan relationen mellem Quiz og AnswerOption beskrives med Fluent API:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Quiz>()
.HasMany(q => q.AnswerOptions)
.WithOne(a => a.Quiz)
.HasForeignKey(a => a.QuizId);
}Code language: C# (cs)
På den måde bliver det mere tydeligt, hvilken relation jeg ønsker, frem for udelukkende at lade EF Core udlede den.
Migrations som bindeled
Når modellen ændres, kan EF Core oprette en migration, som beskriver forskellen mellem den tidligere og den nye model.
Jeg kan eksempelvis oprette en migration med:
dotnet ef migrations add AddQuizAnswerOptionsCode language: Bash (bash)
Migrationen kan derefter anvendes på databasen:
dotnet ef database updateCode language: Bash (bash)
Hvis jeg senere ændrer modellen, kan jeg oprette endnu en migration:
dotnet ef migrations add UpdateQuestItemModelCode language: Bash (bash)
Det betyder, at ændringer i datamodellen kan følge projektets kode og versionsstyring. Det gør systemet mere robust og sikret mod fejl, fordi vi altid kan tilgå versionshistorikken og se hvad er ændret, hvordan, hvorfor og hvornår.
Jeg ser derfor migrations som et bindeled mellem backendmodellen og selve databasen. Samtidig forsøger jeg ikke blot at betragte den genererede migration som korrekt, fordi EF Core har oprettet den. Jeg vil også undersøge, hvilke tabeller, relationer, fremmednøgler og constraints migrationen faktisk medfører.
Backendansvar eller databaseansvar?
Code First ligger efter min opfattelse i et spændingsfelt mellem backendudvikling og databasearbejde.
Arbejdet med entities, DbContext, EF Core-konfiguration og migrations foregår i backendprojektet. Derfor betragter jeg selve brugen af Code First som en naturlig del af mit arbejde med backend.
Valgene får dog direkte konsekvenser for databasen. Måden jeg eksempelvis modellerer QuestItem, Quiz og AnswerOption på, kan påvirke hvilke tabeller og relationer EF Core opretter.
I min rolle som backendudvikler er jeg derfor primært interesseret i, hvordan modeller, relationer og dataadgang struktureres med EF Core, og hvordan ændringer håndteres gennem migrations.
Fra databaseperspektivet bliver spørgsmålet i højere grad, om det resulterende databaseskema faktisk er hensigtsmæssigt.
Det jeg undersøger videre
Mit fokus lige nu er derfor ikke kun at få EF Core til at oprette databasen. Jeg arbejder også videre med at forstå:
- hvordan entities og relationer bør modelleres
- hvordan EF Core oversætter modellerne til et databaseskema
- hvordan
Quiz,ActivityogTaskbedst repræsenteres - hvornår konventioner er tilstrækkelige, og hvornår Fluent API er relevant
- hvordan migrations bør bruges og kontrolleres
- hvordan backendmodellen kan holdes forståelig uden at miste kontrollen over databasedesignet
Code First virker umiddelbart som en praktisk tilgang til QuestGame, fordi datamodellen udvikler sig sammen med backendkoden. Jeg betragter dog stadig tilgangen som noget, jeg skal undersøge og afprøve undervejs, særligt fordi de valg, der foretages i C#-modellerne, også får konsekvenser for den database, EF Core ender med at skabe.

