From d982342ab8fa255678a6103db0402f91cc424484 Mon Sep 17 00:00:00 2001 From: sam Date: Wed, 30 Oct 2024 15:35:23 +0100 Subject: [PATCH] refactor: pass DbContextOptions into context directly turns out efcore doesn't like it when we create a new options instance (which includes a new data source *and* a new logger factory) every single time we create a context. this commit extracts OnConfiguring into static methods which are called when the context is added to the service collection and when it's manually created for migrations and the importer. --- Foxnouns.Backend/Database/DatabaseContext.cs | 76 ++++++++++--------- .../Database/DatabaseServiceExtensions.cs | 21 +++++ .../Extensions/WebApplicationExtensions.cs | 2 +- .../Services/FediverseAuthService.Mastodon.cs | 1 - migrators/NetImporter/NetImporter.cs | 6 +- 5 files changed, 69 insertions(+), 37 deletions(-) create mode 100644 Foxnouns.Backend/Database/DatabaseServiceExtensions.cs diff --git a/Foxnouns.Backend/Database/DatabaseContext.cs b/Foxnouns.Backend/Database/DatabaseContext.cs index 95e0317..e6dc524 100644 --- a/Foxnouns.Backend/Database/DatabaseContext.cs +++ b/Foxnouns.Backend/Database/DatabaseContext.cs @@ -9,10 +9,42 @@ using Npgsql; namespace Foxnouns.Backend.Database; -public class DatabaseContext : DbContext +public class DatabaseContext(DbContextOptions options) : DbContext(options) { - private readonly NpgsqlDataSource _dataSource; - private readonly ILoggerFactory? _loggerFactory; + private static string GenerateConnectionString(Config.DatabaseConfig config) + { + return new NpgsqlConnectionStringBuilder(config.Url) + { + Pooling = config.EnablePooling ?? true, + Timeout = config.Timeout ?? 5, + MaxPoolSize = config.MaxPoolSize ?? 50, + MinPoolSize = 0, + ConnectionPruningInterval = 10, + ConnectionIdleLifetime = 10, + }.ConnectionString; + } + + public static NpgsqlDataSource BuildDataSource(Config config) + { + var dataSourceBuilder = new NpgsqlDataSourceBuilder( + GenerateConnectionString(config.Database) + ); + dataSourceBuilder.UseNodaTime(); + dataSourceBuilder.UseJsonNet(); + return dataSourceBuilder.Build(); + } + + public static DbContextOptionsBuilder BuildOptions( + DbContextOptionsBuilder options, + NpgsqlDataSource dataSource, + ILoggerFactory? loggerFactory + ) => + options + .ConfigureWarnings(c => c.Ignore(CoreEventId.SaveChangesFailed)) + .UseNpgsql(dataSource, o => o.UseNodaTime()) + .UseLoggerFactory(loggerFactory) + .UseSnakeCaseNamingConvention() + .UseExceptionProcessor(); public DbSet Users { get; set; } public DbSet Members { get; set; } @@ -26,36 +58,6 @@ public class DatabaseContext : DbContext public DbSet UserFlags { get; set; } public DbSet MemberFlags { get; set; } - public DatabaseContext(Config config, ILoggerFactory? loggerFactory) - { - var connString = new NpgsqlConnectionStringBuilder(config.Database.Url) - { - Pooling = config.Database.EnablePooling ?? true, - Timeout = config.Database.Timeout ?? 5, - MaxPoolSize = config.Database.MaxPoolSize ?? 50, - MinPoolSize = 0, - ConnectionPruningInterval = 10, - ConnectionIdleLifetime = 10, - }.ConnectionString; - - var dataSourceBuilder = new NpgsqlDataSourceBuilder(connString); - dataSourceBuilder.UseNodaTime(); - dataSourceBuilder.UseJsonNet(); - _dataSource = dataSourceBuilder.Build(); - _loggerFactory = loggerFactory; - } - - protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) => - optionsBuilder - .ConfigureWarnings(c => - c.Ignore(CoreEventId.ManyServiceProvidersCreatedWarning) - .Ignore(CoreEventId.SaveChangesFailed) - ) - .UseNpgsql(_dataSource, o => o.UseNodaTime()) - .UseSnakeCaseNamingConvention() - .UseLoggerFactory(_loggerFactory) - .UseExceptionProcessor(); - protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder) { // Snowflakes are stored as longs @@ -125,6 +127,12 @@ public class DesignTimeDatabaseContextFactory : IDesignTimeDbContextFactory() ?? new(); - return new DatabaseContext(config, null); + var dataSource = DatabaseContext.BuildDataSource(config); + + var options = DatabaseContext + .BuildOptions(new DbContextOptionsBuilder(), dataSource, null) + .Options; + + return new DatabaseContext(options); } } diff --git a/Foxnouns.Backend/Database/DatabaseServiceExtensions.cs b/Foxnouns.Backend/Database/DatabaseServiceExtensions.cs new file mode 100644 index 0000000..4d966bf --- /dev/null +++ b/Foxnouns.Backend/Database/DatabaseServiceExtensions.cs @@ -0,0 +1,21 @@ +using Serilog; + +namespace Foxnouns.Backend.Database; + +public static class DatabaseServiceExtensions +{ + public static IServiceCollection AddFoxnounsDatabase( + this IServiceCollection serviceCollection, + Config config + ) + { + var dataSource = DatabaseContext.BuildDataSource(config); + var loggerFactory = new LoggerFactory().AddSerilog(dispose: false); + + serviceCollection.AddDbContext(options => + DatabaseContext.BuildOptions(options, dataSource, loggerFactory) + ); + + return serviceCollection; + } +} diff --git a/Foxnouns.Backend/Extensions/WebApplicationExtensions.cs b/Foxnouns.Backend/Extensions/WebApplicationExtensions.cs index b2e519d..ce2f59b 100644 --- a/Foxnouns.Backend/Extensions/WebApplicationExtensions.cs +++ b/Foxnouns.Backend/Extensions/WebApplicationExtensions.cs @@ -85,7 +85,7 @@ public static class WebApplicationExtensions services .AddQueue() .AddSmtpMailer(ctx.Configuration) - .AddDbContext() + .AddFoxnounsDatabase(config) .AddMetricServer(o => o.Port = config.Logging.MetricsPort) .AddMinio(c => c.WithEndpoint(config.Storage.Endpoint) diff --git a/Foxnouns.Backend/Services/FediverseAuthService.Mastodon.cs b/Foxnouns.Backend/Services/FediverseAuthService.Mastodon.cs index c8d9dd5..e0e5e98 100644 --- a/Foxnouns.Backend/Services/FediverseAuthService.Mastodon.cs +++ b/Foxnouns.Backend/Services/FediverseAuthService.Mastodon.cs @@ -2,7 +2,6 @@ using System.Net; using System.Web; using Foxnouns.Backend.Database; using Foxnouns.Backend.Database.Models; -using Minio.DataModel.ILM; using Duration = NodaTime.Duration; using J = System.Text.Json.Serialization.JsonPropertyNameAttribute; diff --git a/migrators/NetImporter/NetImporter.cs b/migrators/NetImporter/NetImporter.cs index 1f1bd9a..7a0ddf6 100644 --- a/migrators/NetImporter/NetImporter.cs +++ b/migrators/NetImporter/NetImporter.cs @@ -56,7 +56,11 @@ internal static class NetImporter var loggerFactory = new LoggerFactory().AddSerilog(Log.Logger); var config = new Config { Database = new Config.DatabaseConfig { Url = connString } }; - var db = new DatabaseContext(config, loggerFactory); + var dataSource = DatabaseContext.BuildDataSource(config); + var options = DatabaseContext + .BuildOptions(new DbContextOptionsBuilder(), dataSource, loggerFactory) + .Options; + var db = new DatabaseContext(options); if ((await db.Database.GetPendingMigrationsAsync()).Any()) {