diff --git a/src/ClaudeDo.App/Program.cs b/src/ClaudeDo.App/Program.cs index c057463..68dbd87 100644 --- a/src/ClaudeDo.App/Program.cs +++ b/src/ClaudeDo.App/Program.cs @@ -21,7 +21,8 @@ sealed class Program using (var scope = services.CreateScope()) { - scope.ServiceProvider.GetRequiredService().Database.Migrate(); + ClaudeDoDbContext.MigrateAndConfigure( + scope.ServiceProvider.GetRequiredService()); } try diff --git a/src/ClaudeDo.Data/ClaudeDoDbContext.cs b/src/ClaudeDo.Data/ClaudeDoDbContext.cs index 99a3086..661e136 100644 --- a/src/ClaudeDo.Data/ClaudeDoDbContext.cs +++ b/src/ClaudeDo.Data/ClaudeDoDbContext.cs @@ -1,5 +1,7 @@ using ClaudeDo.Data.Models; using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage; namespace ClaudeDo.Data; @@ -19,4 +21,44 @@ public class ClaudeDoDbContext : DbContext { 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) + { + // If the 'lists' table exists but __EFMigrationsHistory does not, + // this is a pre-EF database. Baseline the InitialCreate migration. + var conn = db.Database.GetDbConnection(); + conn.Open(); + 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) + { + // Create the history table and mark InitialCreate as applied. + 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(); + } + } + conn.Close(); + + db.Database.Migrate(); + db.Database.ExecuteSqlRaw("PRAGMA journal_mode=WAL"); + } } diff --git a/src/ClaudeDo.Data/Repositories/TaskRepository.cs b/src/ClaudeDo.Data/Repositories/TaskRepository.cs index d7cc260..7fddc90 100644 --- a/src/ClaudeDo.Data/Repositories/TaskRepository.cs +++ b/src/ClaudeDo.Data/Repositories/TaskRepository.cs @@ -38,7 +38,7 @@ public sealed class TaskRepository { return await _context.Tasks .Where(t => t.ListId == listId) - .OrderByDescending(t => t.CreatedAt) + .OrderBy(t => t.CreatedAt) .ToListAsync(ct); } diff --git a/src/ClaudeDo.Installer/Steps/InitDatabaseStep.cs b/src/ClaudeDo.Installer/Steps/InitDatabaseStep.cs index 7b6b028..007b36c 100644 --- a/src/ClaudeDo.Installer/Steps/InitDatabaseStep.cs +++ b/src/ClaudeDo.Installer/Steps/InitDatabaseStep.cs @@ -19,7 +19,7 @@ public sealed class InitDatabaseStep : IInstallStep .UseSqlite($"Data Source={expandedPath}") .Options; using var context = new ClaudeDoDbContext(options); - context.Database.Migrate(); + ClaudeDoDbContext.MigrateAndConfigure(context); progress.Report("Schema applied successfully"); return Task.FromResult(StepResult.Ok()); diff --git a/src/ClaudeDo.Worker/Program.cs b/src/ClaudeDo.Worker/Program.cs index 68a80aa..fbf8c9a 100644 --- a/src/ClaudeDo.Worker/Program.cs +++ b/src/ClaudeDo.Worker/Program.cs @@ -17,16 +17,8 @@ builder.Host.UseWindowsService(o => o.ServiceName = "ClaudeDoWorker"); builder.Services.AddDbContextFactory(opt => opt.UseSqlite($"Data Source={cfg.DbPath}")); -builder.Services.AddDbContext(opt => - opt.UseSqlite($"Data Source={cfg.DbPath}")); builder.Services.AddSingleton(cfg); -builder.Services.AddScoped(); -builder.Services.AddScoped(); -builder.Services.AddScoped(); -builder.Services.AddScoped(); -builder.Services.AddScoped(); -builder.Services.AddScoped(); builder.Services.AddHostedService(); builder.Services.AddSignalR(); @@ -54,7 +46,8 @@ var app = builder.Build(); using (var scope = app.Services.CreateScope()) { - scope.ServiceProvider.GetRequiredService().Database.Migrate(); + ClaudeDoDbContext.MigrateAndConfigure( + scope.ServiceProvider.GetRequiredService()); } app.MapHub("/hub");