using ClaudeDo.Data.Models; using ClaudeDo.Data.Seeding; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Storage; namespace ClaudeDo.Data; public class ClaudeDoDbContext : DbContext { public ClaudeDoDbContext(DbContextOptions options) : base(options) { } public DbSet Tasks => Set(); public DbSet Lists => Set(); public DbSet Tags => Set(); public DbSet ListConfigs => Set(); public DbSet Worktrees => Set(); public DbSet TaskRuns => Set(); public DbSet Subtasks => Set(); public DbSet AppSettings => Set(); protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.ApplyConfigurationsFromAssembly(typeof(ClaudeDoDbContext).Assembly); } /// /// Applies EF Core migrations and sets WAL mode. Safe for both fresh and existing databases. /// Existing databases (created by the old schema.sql) have their tables but no /// __EFMigrationsHistory — this method detects that case and baselines the initial /// migration so EF skips re-creating tables that already exist. /// public static void MigrateAndConfigure(ClaudeDoDbContext db) { var conn = db.Database.GetDbConnection(); try { conn.Open(); // Set WAL FIRST, before migrations — prevents write-lock contention // when UI and Worker start simultaneously. using (var walCmd = conn.CreateCommand()) { walCmd.CommandText = "PRAGMA journal_mode=wal;"; walCmd.ExecuteNonQuery(); } // Enable FK enforcement — SQLite defaults to OFF per connection. using (var fkCmd = conn.CreateCommand()) { fkCmd.CommandText = "PRAGMA foreign_keys=ON;"; fkCmd.ExecuteNonQuery(); } // If the 'lists' table exists but __EFMigrationsHistory does not, // this is a pre-EF database. Baseline the InitialCreate migration. using (var cmd = conn.CreateCommand()) { cmd.CommandText = "SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='lists'"; var hasLists = Convert.ToInt64(cmd.ExecuteScalar()) > 0; cmd.CommandText = "SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='__EFMigrationsHistory'"; var hasHistory = Convert.ToInt64(cmd.ExecuteScalar()) > 0; if (hasLists && !hasHistory) { cmd.CommandText = """ CREATE TABLE "__EFMigrationsHistory" ( "MigrationId" TEXT NOT NULL CONSTRAINT "PK___EFMigrationsHistory" PRIMARY KEY, "ProductVersion" TEXT NOT NULL ); INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") VALUES ('20260416064948_InitialCreate', '8.0.11'); """; cmd.ExecuteNonQuery(); } } } finally { conn.Close(); } db.Database.Migrate(); DefaultListsSeeder.SeedAsync(db).GetAwaiter().GetResult(); } }