diff --git a/services/Directory/FilterLists.Directory.Infrastructure.MigrationService/Worker.cs b/services/Directory/FilterLists.Directory.Infrastructure.MigrationService/Worker.cs
index d1008a3dc..36fcecfc2 100644
--- a/services/Directory/FilterLists.Directory.Infrastructure.MigrationService/Worker.cs
+++ b/services/Directory/FilterLists.Directory.Infrastructure.MigrationService/Worker.cs
@@ -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
+/// https://learn.microsoft.com/en-us/dotnet/aspire/database/ef-core-migrations#create-the-migration-service
+public class Worker(
+ IServiceProvider serviceProvider,
+ IHostApplicationLifetime hostApplicationLifetime,
+ IHostEnvironment hostEnvironment) : BackgroundService
{
- private readonly ILogger _logger;
+ private const string ActivitySourceName = "Migrations";
+ private static readonly ActivitySource ActivitySource = new(ActivitySourceName);
- public Worker(ILogger 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();
+
+ 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();
+
+ 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);
+ });
}
}
\ No newline at end of file
diff --git a/services/Directory/FilterLists.Directory.Infrastructure/Persistence/SeedExtensions.cs b/services/Directory/FilterLists.Directory.Infrastructure/Persistence/SeedExtensions.cs
index 3d1c08a25..edc589dfd 100644
--- a/services/Directory/FilterLists.Directory.Infrastructure/Persistence/SeedExtensions.cs
+++ b/services/Directory/FilterLists.Directory.Infrastructure/Persistence/SeedExtensions.cs
@@ -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();
- await db.Database.MigrateAsync();
- }
-}
-
internal static class SeedConfigurationExtensions
{
private static readonly JsonSerializerOptions CamelCaseJsonSerializerOptions =
diff --git a/services/FilterLists.AppHost/FilterLists.AppHost.csproj b/services/FilterLists.AppHost/FilterLists.AppHost.csproj
index 9ef25c2e6..ddcb87976 100644
--- a/services/FilterLists.AppHost/FilterLists.AppHost.csproj
+++ b/services/FilterLists.AppHost/FilterLists.AppHost.csproj
@@ -11,6 +11,7 @@
+
diff --git a/services/FilterLists.AppHost/Program.cs b/services/FilterLists.AppHost/Program.cs
index 6b0b9b1ed..96ab2060d 100644
--- a/services/FilterLists.AppHost/Program.cs
+++ b/services/FilterLists.AppHost/Program.cs
@@ -9,6 +9,10 @@
.PublishAsConnectionString() // customized for free tier, don't trust Aspire to provision db
.AddDatabase("directorydb");
+builder.AddProject("directorymigrationservice")
+ .WithReference(directoryDb)
+ .WithReference(appInsights);
+
var directoryApi = builder.AddProject("directoryapi")
.WithReference(directoryDb)
.WithReference(appInsights);