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.
This commit is contained in:
parent
0077a165b5
commit
d982342ab8
5 changed files with 69 additions and 37 deletions
|
@ -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<User> Users { get; set; }
|
||||
public DbSet<Member> Members { get; set; }
|
||||
|
@ -26,36 +58,6 @@ public class DatabaseContext : DbContext
|
|||
public DbSet<UserFlag> UserFlags { get; set; }
|
||||
public DbSet<MemberFlag> 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<Data
|
|||
// Get the configuration as our config class
|
||||
.Get<Config>() ?? new();
|
||||
|
||||
return new DatabaseContext(config, null);
|
||||
var dataSource = DatabaseContext.BuildDataSource(config);
|
||||
|
||||
var options = DatabaseContext
|
||||
.BuildOptions(new DbContextOptionsBuilder(), dataSource, null)
|
||||
.Options;
|
||||
|
||||
return new DatabaseContext(options);
|
||||
}
|
||||
}
|
||||
|
|
21
Foxnouns.Backend/Database/DatabaseServiceExtensions.cs
Normal file
21
Foxnouns.Backend/Database/DatabaseServiceExtensions.cs
Normal file
|
@ -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<DatabaseContext>(options =>
|
||||
DatabaseContext.BuildOptions(options, dataSource, loggerFactory)
|
||||
);
|
||||
|
||||
return serviceCollection;
|
||||
}
|
||||
}
|
|
@ -85,7 +85,7 @@ public static class WebApplicationExtensions
|
|||
services
|
||||
.AddQueue()
|
||||
.AddSmtpMailer(ctx.Configuration)
|
||||
.AddDbContext<DatabaseContext>()
|
||||
.AddFoxnounsDatabase(config)
|
||||
.AddMetricServer(o => o.Port = config.Logging.MetricsPort)
|
||||
.AddMinio(c =>
|
||||
c.WithEndpoint(config.Storage.Endpoint)
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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())
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue