using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; namespace PLib.Infrastructure.Persistence; /// /// Brings the local database up to date before the first window is shown. /// public sealed class DatabaseInitializer( IServiceScopeFactory scopeFactory, ILogger logger) : IHostedService { public async Task StartAsync(CancellationToken cancellationToken) { await using var scope = scopeFactory.CreateAsyncScope(); var dbContext = scope.ServiceProvider.GetRequiredService(); if (await IsPreMigrationDatabaseAsync(dbContext, cancellationToken)) { // Earlier builds created the schema straight from the model, so there is no // migration history to continue from and no honest way to baseline one — the // columns a baseline would claim exist do not. The database is a cache over the // file system, so throwing it away costs one rescan; poster frames are keyed by // file and survive untouched. logger.LogWarning("Replacing a database created before migrations were introduced"); await dbContext.Database.EnsureDeletedAsync(cancellationToken); } var pending = await dbContext.Database.GetPendingMigrationsAsync(cancellationToken); var pendingCount = pending.Count(); if (pendingCount > 0) { logger.LogInformation("Applying {Count} database migration(s)", pendingCount); } await dbContext.Database.MigrateAsync(cancellationToken); logger.LogInformation("Library database ready"); } public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask; /// /// True when the file holds a schema but no migration history — the shape earlier /// versions left behind. /// private static async Task IsPreMigrationDatabaseAsync( LibraryDbContext dbContext, CancellationToken cancellationToken) { if (!await dbContext.Database.CanConnectAsync(cancellationToken)) { return false; } var applied = await dbContext.Database.GetAppliedMigrationsAsync(cancellationToken); if (applied.Any()) { return false; } var tables = await dbContext.Database .SqlQuery($"SELECT name FROM sqlite_master WHERE type = 'table' AND name = 'Videos'") .ToListAsync(cancellationToken); return tables.Count > 0; } }