72 lines
2.6 KiB
C#
72 lines
2.6 KiB
C#
using Microsoft.EntityFrameworkCore;
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
using Microsoft.Extensions.Hosting;
|
|
using Microsoft.Extensions.Logging;
|
|
|
|
namespace PLib.Infrastructure.Persistence;
|
|
|
|
/// <summary>
|
|
/// Brings the local database up to date before the first window is shown.
|
|
/// </summary>
|
|
public sealed class DatabaseInitializer(
|
|
IServiceScopeFactory scopeFactory,
|
|
ILogger<DatabaseInitializer> logger) : IHostedService
|
|
{
|
|
public async Task StartAsync(CancellationToken cancellationToken)
|
|
{
|
|
await using var scope = scopeFactory.CreateAsyncScope();
|
|
var dbContext = scope.ServiceProvider.GetRequiredService<LibraryDbContext>();
|
|
|
|
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;
|
|
|
|
/// <summary>
|
|
/// True when the file holds a schema but no migration history — the shape earlier
|
|
/// versions left behind.
|
|
/// </summary>
|
|
private static async Task<bool> 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<string>($"SELECT name FROM sqlite_master WHERE type = 'table' AND name = 'Videos'")
|
|
.ToListAsync(cancellationToken);
|
|
|
|
return tables.Count > 0;
|
|
}
|
|
}
|