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;
}
}