mirror of
https://github.com/collinbarrett/FilterLists.git
synced 2026-03-11 09:04:27 +00:00
feat(dir): ✨ apply migrations on startup
This commit is contained in:
parent
93768e4571
commit
cb4435f331
4 changed files with 59 additions and 24 deletions
|
|
@ -1,21 +1,65 @@
|
|||
using System.Diagnostics;
|
||||
using FilterLists.Directory.Infrastructure.Persistence.Queries.Context;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Storage;
|
||||
using OpenTelemetry.Trace;
|
||||
|
||||
namespace FilterLists.Directory.Infrastructure.MigrationService;
|
||||
|
||||
public class Worker : BackgroundService
|
||||
/// <remarks>https://learn.microsoft.com/en-us/dotnet/aspire/database/ef-core-migrations#create-the-migration-service</remarks>
|
||||
public class Worker(
|
||||
IServiceProvider serviceProvider,
|
||||
IHostApplicationLifetime hostApplicationLifetime,
|
||||
IHostEnvironment hostEnvironment) : BackgroundService
|
||||
{
|
||||
private readonly ILogger<Worker> _logger;
|
||||
private const string ActivitySourceName = "Migrations";
|
||||
private static readonly ActivitySource ActivitySource = new(ActivitySourceName);
|
||||
|
||||
public Worker(ILogger<Worker> logger)
|
||||
protected override async Task ExecuteAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
_logger = logger;
|
||||
// allow SQL Server container time to start
|
||||
await Task.Delay(3000, cancellationToken);
|
||||
|
||||
// ReSharper disable once ExplicitCallerInfoArgument
|
||||
using var activity = ActivitySource.StartActivity("Migrating database", ActivityKind.Client);
|
||||
|
||||
try
|
||||
{
|
||||
using var scope = serviceProvider.CreateScope();
|
||||
var dbContext = scope.ServiceProvider.GetRequiredService<QueryDbContext>();
|
||||
|
||||
if (hostEnvironment.IsDevelopment()) await EnsureDatabaseAsync(dbContext, cancellationToken);
|
||||
await RunMigrationAsync(dbContext, cancellationToken);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
activity?.RecordException(ex);
|
||||
throw;
|
||||
}
|
||||
|
||||
hostApplicationLifetime.StopApplication();
|
||||
}
|
||||
|
||||
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||
private static async Task EnsureDatabaseAsync(QueryDbContext dbContext, CancellationToken cancellationToken)
|
||||
{
|
||||
while (!stoppingToken.IsCancellationRequested)
|
||||
var dbCreator = dbContext.GetService<IRelationalDatabaseCreator>();
|
||||
|
||||
var strategy = dbContext.Database.CreateExecutionStrategy();
|
||||
await strategy.ExecuteAsync(async () =>
|
||||
{
|
||||
if (_logger.IsEnabled(LogLevel.Information))
|
||||
_logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
|
||||
await Task.Delay(1000, stoppingToken);
|
||||
}
|
||||
if (!await dbCreator.ExistsAsync(cancellationToken)) await dbCreator.CreateAsync(cancellationToken);
|
||||
});
|
||||
}
|
||||
|
||||
private static async Task RunMigrationAsync(QueryDbContext dbContext, CancellationToken cancellationToken)
|
||||
{
|
||||
var strategy = dbContext.Database.CreateExecutionStrategy();
|
||||
await strategy.ExecuteAsync(async () =>
|
||||
{
|
||||
await using var transaction = await dbContext.Database.BeginTransactionAsync(cancellationToken);
|
||||
await dbContext.Database.MigrateAsync(cancellationToken);
|
||||
await transaction.CommitAsync(cancellationToken);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -1,22 +1,8 @@
|
|||
using System.Text.Json;
|
||||
using FilterLists.Directory.Infrastructure.Persistence.Queries.Context;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
|
||||
namespace FilterLists.Directory.Infrastructure.Persistence;
|
||||
|
||||
public static class SeedExtension
|
||||
{
|
||||
public static async Task MigrateAsync(this IHost host)
|
||||
{
|
||||
using var scope = host.Services.CreateScope();
|
||||
var db = scope.ServiceProvider.GetRequiredService<QueryDbContext>();
|
||||
await db.Database.MigrateAsync();
|
||||
}
|
||||
}
|
||||
|
||||
internal static class SeedConfigurationExtensions
|
||||
{
|
||||
private static readonly JsonSerializerOptions CamelCaseJsonSerializerOptions =
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Directory\FilterLists.Directory.Api\FilterLists.Directory.Api.csproj"/>
|
||||
<ProjectReference Include="..\Directory\FilterLists.Directory.Infrastructure.MigrationService\FilterLists.Directory.Infrastructure.MigrationService.csproj"/>
|
||||
<ProjectReference Include="..\FilterLists.Web\FilterLists.Web.csproj"/>
|
||||
</ItemGroup>
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,10 @@
|
|||
.PublishAsConnectionString() // customized for free tier, don't trust Aspire to provision db
|
||||
.AddDatabase("directorydb");
|
||||
|
||||
builder.AddProject<FilterLists_Directory_Infrastructure_MigrationService>("directorymigrationservice")
|
||||
.WithReference(directoryDb)
|
||||
.WithReference(appInsights);
|
||||
|
||||
var directoryApi = builder.AddProject<FilterLists_Directory_Api>("directoryapi")
|
||||
.WithReference(directoryDb)
|
||||
.WithReference(appInsights);
|
||||
|
|
|
|||
Loading…
Reference in a new issue