Problem
The project has two related issues with its EF Core setup:
-
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.
-
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.
-
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
Part 2 — Migrations
Part 3 — Tests
All
References
Problem
The project has two related issues with its EF Core setup:
Startup never applied migrations. The app relied on a pre-seeded
storage/players-sqlite3.dbfile committed as a binary blob, baked into the Docker image, and kept in sync manually viascripts/run-migrations-and-copy-database.sh. Migrations existed purely as a code artifact and were never exercised at runtime.Migrations call live application code.
SeedStarting11andSeedSubstitutescallPlayerData.MakeStarting11WithId()directly inside the migration class, bypassing EF Core's generation. This violates the principle that migrations must be immutable snapshots: ifPlayerDataever changes, re-running the migration chain from scratch produces different results than when the migration was first applied.NormalizePlayerDatasetexists as a patch migration to fix mistakes in the earlier seeds, adding noise to the history.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)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:
InitialCreateCreateTable+SquadNumberunique indexSeedStarting11HasData()configured inOnModelCreatingfor the 11 starters → EF Core generates literalInsertDatacallsSeedSubstitutesHasData()extended with the 14 substitutes → EF Core generatesInsertDatafor thoseNormalizePlayerDatasetis not recreated — its corrections (final UUIDs, team names) are absorbed into the seed data from the start.PlayerData.MakeStarting11WithId()and anyWithIdvariants are removed;PlayerDatasurvives as application code feedingHasData()and used byPlayerFakesin tests.Part 3 — Fix
DatabaseFakesReplace
CreateTable()(placeholder schema) andSeed()(manual insert, bypasses migrations) withcontext.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 inProgram.csbeforeapp.RunAsync()storage/players-sqlite3.dbis removed from the repositorystorage/*.dbis added to.gitignoreDockerfileno longer copies a pre-seeded database filescripts/run-migrations-and-copy-database.shis removeddocker compose upstarts cleanly and the app serves all 25 players with no manual setupREADME.mdreflects the new startup behaviorCHANGELOG.mdupdated under[Unreleased] → ChangedPart 2 — Migrations
Migrations/contains exactly three migrations:InitialCreate(DDL),SeedStarting11(DML),SeedSubstitutes(DML)InsertDataliterals fromHasData()NormalizePlayerDatasetdoes not existHasData()inOnModelCreatingis the single source of truth for seed data at the schema levelPlayerData.MakeStarting11WithId()and equivalentWithIdvariants are removedPart 3 — Tests
DatabaseFakes.CreateTable()is replaced withcontext.Database.MigrateAsync()DatabaseFakes.Seed()is removedAll
dotnet build --configuration Releasepassesdotnet test --settings .runsettings)CHANGELOG.mdupdatedReferences
HasData(): https://learn.microsoft.com/en-us/ef/core/modeling/data-seedingsrc/.../Migrations/(InitialCreate, SeedStarting11, SeedSubstitutes, NormalizePlayerDataset)