Skip to content

Switch from EnsureCreated() to EF Migrations for schema updates #468

@tylerkron

Description

@tylerkron

Problem

LoggingContext calls Database.EnsureCreated() in its constructor, which:

  1. Ignores the migrations folder — schema changes defined as migrations (like the composite index added in chore: viewport-aware downsampling and 60fps interaction for minimap #467) are never applied to existing databases
  2. Runs on every context creation — wasteful since the DB already exists after the first call
  3. Conflicts with the existing migration20250812090000_InitialSQLiteMigration.cs exists but never runs because EnsureCreated() takes precedence

This means any schema change (new index, column, table) only takes effect on fresh installs, not for existing users.

Concrete example

PR #467 added a composite index IX_Samples_SessionTime on (LoggingSessionID, TimestampTicks) to speed up ordered session queries. This index will never be created on existing user databases.

Proposed Solution

Replace EnsureCreated() with Database.Migrate() at app startup.

Steps

  1. Remove Database.EnsureCreated() from the LoggingContext constructor
  2. Add a one-time Database.Migrate() call in App.OnStartup() (before any other DB access)
  3. Create a new migration for the composite index:
    dotnet ef migrations add AddSamplesSessionTimeIndex
  4. Handle the upgrade path for existing databases that were created by EnsureCreated() (no __EFMigrationsHistory table):
    // At startup, before Migrate():
    using var context = factory.CreateDbContext();
    if (!context.Database.GetAppliedMigrations().Any()
        && context.Database.GetPendingMigrations().Any())
    {
        // Existing DB created by EnsureCreated — seed migration history
        // so Migrate() doesn't try to recreate existing tables
        context.Database.ExecuteSqlRaw(
            "CREATE TABLE IF NOT EXISTS __EFMigrationsHistory " +
            "(MigrationId TEXT NOT NULL PRIMARY KEY, ProductVersion TEXT NOT NULL)");
        context.Database.ExecuteSqlRaw(
            "INSERT OR IGNORE INTO __EFMigrationsHistory " +
            "VALUES ('20250812090000_InitialSQLiteMigration', '9.0.0')");
    }
    context.Database.Migrate();
  5. Update test setup to use Migrate() instead of EnsureCreated()

Risks & Mitigation

Risk Severity Mitigation
Existing users' DBs fail on upgrade High Seed __EFMigrationsHistory with initial migration before calling Migrate()
Data loss if migration fails Medium Back up the DB file at startup before first Migrate() call (SQLite = single file copy)
Missed code paths still calling EnsureCreated() Low Search entire codebase — currently only 1 call site

Key files

  • Daqifi.Desktop/Loggers/LoggingContext.cs — remove EnsureCreated() from constructor
  • Daqifi.Desktop/App.xaml.cs — add startup migration logic
  • Daqifi.Desktop/Migrations/ — existing migration folder, add new migration for index

Context

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions