Skip to content

Replace pre-seeded database file with EF Core auto-migration at startup and recreate migrations using HasData() #459

@nanotaboada

Description

@nanotaboada

Problem

The project has two related issues with its EF Core setup:

  1. Startup never applied migrations. The app relied on a pre-seeded storage/players-sqlite3.db file committed as a binary blob, baked into the Docker image, and kept in sync manually via scripts/run-migrations-and-copy-database.sh. Migrations existed purely as a code artifact and were never exercised at runtime.

  2. Migrations call live application code. SeedStarting11 and SeedSubstitutes call PlayerData.MakeStarting11WithId() directly inside the migration class, bypassing EF Core's generation. This violates the principle that migrations must be immutable snapshots: if PlayerData ever changes, re-running the migration chain from scratch produces different results than when the migration was first applied. NormalizePlayerDataset exists as a patch migration to fix mistakes in the earlier seeds, adding noise to the history.

  3. DatabaseFakes.CreateTable() has a placeholder schema (/* ... other columns ... */) and is not the real schema. Test setup bypasses migrations entirely.

Additionally, this is a prerequisite for #249 (PostgreSQL support): PostgreSQL is client-server and cannot rely on a committed file — both providers must share the same migration-based initialization path.

Proposed Solution

Part 1 — Wire MigrateAsync() at startup ✅ (already implemented)

await using (var scope = app.Services.CreateAsyncScope())
{
    var db = scope.ServiceProvider.GetRequiredService<PlayerDbContext>();
    await db.Database.MigrateAsync();
}

Remove the committed database file, the manual copy script, and the Dockerfile db copy step.

Part 2 — Recreate migrations using HasData()

Delete all existing migrations and recreate three clean ones that preserve the educational intent (showing incremental migrations) while following the idiomatic EF Core approach:

# Name Type How
1 InitialCreate DDL CreateTable + SquadNumber unique index
2 SeedStarting11 DML HasData() configured in OnModelCreating for the 11 starters → EF Core generates literal InsertData calls
3 SeedSubstitutes DML HasData() extended with the 14 substitutes → EF Core generates InsertData for those

NormalizePlayerDataset is not recreated — its corrections (final UUIDs, team names) are absorbed into the seed data from the start. PlayerData.MakeStarting11WithId() and any WithId variants are removed; PlayerData survives as application code feeding HasData() and used by PlayerFakes in tests.

Part 3 — Fix DatabaseFakes

Replace CreateTable() (placeholder schema) and Seed() (manual insert, bypasses migrations) with context.Database.MigrateAsync(), so test setup exercises the real migration chain on in-memory SQLite.

Acceptance Criteria

Part 1 — Startup migration

  • db.Database.MigrateAsync() is called in Program.cs before app.RunAsync()
  • storage/players-sqlite3.db is removed from the repository
  • storage/*.db is added to .gitignore
  • Dockerfile no longer copies a pre-seeded database file
  • scripts/run-migrations-and-copy-database.sh is removed
  • docker compose up starts cleanly and the app serves all 25 players with no manual setup
  • README.md reflects the new startup behavior
  • CHANGELOG.md updated under [Unreleased] → Changed

Part 2 — Migrations

  • Migrations/ contains exactly three migrations: InitialCreate (DDL), SeedStarting11 (DML), SeedSubstitutes (DML)
  • No migration calls any application method — all seed data is generated by EF Core as InsertData literals from HasData()
  • NormalizePlayerDataset does not exist
  • HasData() in OnModelCreating is the single source of truth for seed data at the schema level
  • PlayerData.MakeStarting11WithId() and equivalent WithId variants are removed

Part 3 — Tests

  • DatabaseFakes.CreateTable() is replaced with context.Database.MigrateAsync()
  • DatabaseFakes.Seed() is removed

All

  • dotnet build --configuration Release passes
  • All existing tests pass (dotnet test --settings .runsettings)
  • CHANGELOG.md updated

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    .NETPull requests that update .NET codeenhancementNew feature or requestpriority highImportant for production readiness. Schedule for current milestone.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions