Fix DbContext configuration nightmares.

Thanks to julian figuring out IDesignTimeDbContextFactory exists in #6327.

All this DbContext configuration and options setup stuff is insane. Microsoft should be absolutely ashamed for coming up with this load of garbage.
This commit is contained in:
Pieter-Jan Briers
2022-02-03 03:13:34 +01:00
parent 32a1f6ae93
commit 4da56becab
8 changed files with 44 additions and 46 deletions

View File

@@ -0,0 +1,25 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design;
// ReSharper disable UnusedType.Global
namespace Content.Server.Database;
public sealed class DesignTimeContextFactoryPostgres : IDesignTimeDbContextFactory<PostgresServerDbContext>
{
public PostgresServerDbContext CreateDbContext(string[] args)
{
var optionsBuilder = new DbContextOptionsBuilder<PostgresServerDbContext>();
optionsBuilder.UseNpgsql(args[0]);
return new PostgresServerDbContext(optionsBuilder.Options);
}
}
public sealed class DesignTimeContextFactorySqlite : IDesignTimeDbContextFactory<SqliteServerDbContext>
{
public SqliteServerDbContext CreateDbContext(string[] args)
{
var optionsBuilder = new DbContextOptionsBuilder<SqliteServerDbContext>();
optionsBuilder.UseSqlite(args[0]);
return new SqliteServerDbContext(optionsBuilder.Options);
}
}

View File

@@ -11,22 +11,10 @@ namespace Content.Server.Database
{ {
public abstract class ServerDbContext : DbContext public abstract class ServerDbContext : DbContext
{ {
/// <summary> protected ServerDbContext(DbContextOptions options) : base(options)
/// The "dotnet ef" CLI tool uses the parameter-less constructor.
/// When that happens we want to supply the <see cref="DbContextOptions"/> via <see cref="DbContext.OnConfiguring"/>.
/// To use the context within the application, the options need to be passed the constructor instead.
/// </summary>
protected readonly bool InitializedWithOptions;
public ServerDbContext()
{ {
} }
public ServerDbContext(DbContextOptions<ServerDbContext> options) : base(options)
{
InitializedWithOptions = true;
}
public DbSet<Preference> Preference { get; set; } = null!; public DbSet<Preference> Preference { get; set; } = null!;
public DbSet<Profile> Profile { get; set; } = null!; public DbSet<Profile> Profile { get; set; } = null!;
public DbSet<AssignedUserId> AssignedUserId { get; set; } = null!; public DbSet<AssignedUserId> AssignedUserId { get; set; } = null!;

View File

@@ -8,21 +8,17 @@ namespace Content.Server.Database
{ {
public sealed class PostgresServerDbContext : ServerDbContext public sealed class PostgresServerDbContext : ServerDbContext
{ {
// This is used by the "dotnet ef" CLI tool.
public PostgresServerDbContext()
{
}
static PostgresServerDbContext() static PostgresServerDbContext()
{ {
AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true); AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true);
} }
public PostgresServerDbContext(DbContextOptions<PostgresServerDbContext> options) : base(options)
{
}
protected override void OnConfiguring(DbContextOptionsBuilder options) protected override void OnConfiguring(DbContextOptionsBuilder options)
{ {
if (!InitializedWithOptions)
options.UseNpgsql("dummy connection string");
options.ReplaceService<IRelationalTypeMappingSource, CustomNpgsqlTypeMappingSource>(); options.ReplaceService<IRelationalTypeMappingSource, CustomNpgsqlTypeMappingSource>();
((IDbContextOptionsBuilderInfrastructure) options).AddOrUpdateExtension(new SnakeCaseExtension()); ((IDbContextOptionsBuilderInfrastructure) options).AddOrUpdateExtension(new SnakeCaseExtension());
@@ -41,10 +37,6 @@ namespace Content.Server.Database
#endif #endif
} }
public PostgresServerDbContext(DbContextOptions<ServerDbContext> options) : base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder) protected override void OnModelCreating(ModelBuilder modelBuilder)
{ {
base.OnModelCreating(modelBuilder); base.OnModelCreating(modelBuilder);

View File

@@ -13,15 +13,12 @@ namespace Content.Server.Database
{ {
public sealed class SqliteServerDbContext : ServerDbContext public sealed class SqliteServerDbContext : ServerDbContext
{ {
public SqliteServerDbContext() public SqliteServerDbContext(DbContextOptions<SqliteServerDbContext> options) : base(options)
{ {
} }
protected override void OnConfiguring(DbContextOptionsBuilder options) protected override void OnConfiguring(DbContextOptionsBuilder options)
{ {
if (!InitializedWithOptions)
options.UseSqlite("dummy connection string");
((IDbContextOptionsBuilderInfrastructure) options).AddOrUpdateExtension(new SnakeCaseExtension()); ((IDbContextOptionsBuilderInfrastructure) options).AddOrUpdateExtension(new SnakeCaseExtension());
options.ConfigureWarnings(x => options.ConfigureWarnings(x =>
@@ -70,10 +67,6 @@ namespace Content.Server.Database
.HasConversion(jsonConverter); .HasConversion(jsonConverter);
} }
public SqliteServerDbContext(DbContextOptions<ServerDbContext> options) : base(options)
{
}
private static string InetToString(IPAddress address, int mask) { private static string InetToString(IPAddress address, int mask) {
if (address.IsIPv4MappedToIPv6) if (address.IsIPv4MappedToIPv6)
{ {

View File

@@ -180,12 +180,12 @@ namespace Content.Server.Database
switch (engine) switch (engine)
{ {
case "sqlite": case "sqlite":
var options = CreateSqliteOptions(); var sqliteOptions = CreateSqliteOptions();
_db = new ServerDbSqlite(options); _db = new ServerDbSqlite(sqliteOptions);
break; break;
case "postgres": case "postgres":
options = CreatePostgresOptions(); var pgOptions = CreatePostgresOptions();
_db = new ServerDbPostgres(options); _db = new ServerDbPostgres(pgOptions);
break; break;
default: default:
throw new InvalidDataException($"Unknown database engine {engine}."); throw new InvalidDataException($"Unknown database engine {engine}.");
@@ -394,7 +394,7 @@ namespace Content.Server.Database
return _db.RemoveFromWhitelistAsync(player); return _db.RemoveFromWhitelistAsync(player);
} }
private DbContextOptions<ServerDbContext> CreatePostgresOptions() private DbContextOptions<PostgresServerDbContext> CreatePostgresOptions()
{ {
var host = _cfg.GetCVar(CCVars.DatabasePgHost); var host = _cfg.GetCVar(CCVars.DatabasePgHost);
var port = _cfg.GetCVar(CCVars.DatabasePgPort); var port = _cfg.GetCVar(CCVars.DatabasePgPort);
@@ -402,7 +402,7 @@ namespace Content.Server.Database
var user = _cfg.GetCVar(CCVars.DatabasePgUsername); var user = _cfg.GetCVar(CCVars.DatabasePgUsername);
var pass = _cfg.GetCVar(CCVars.DatabasePgPassword); var pass = _cfg.GetCVar(CCVars.DatabasePgPassword);
var builder = new DbContextOptionsBuilder<ServerDbContext>(); var builder = new DbContextOptionsBuilder<PostgresServerDbContext>();
var connectionString = new NpgsqlConnectionStringBuilder var connectionString = new NpgsqlConnectionStringBuilder
{ {
Host = host, Host = host,
@@ -419,9 +419,9 @@ namespace Content.Server.Database
return builder.Options; return builder.Options;
} }
private DbContextOptions<ServerDbContext> CreateSqliteOptions() private DbContextOptions<SqliteServerDbContext> CreateSqliteOptions()
{ {
var builder = new DbContextOptionsBuilder<ServerDbContext>(); var builder = new DbContextOptionsBuilder<SqliteServerDbContext>();
var configPreferencesDbPath = _cfg.GetCVar(CCVars.DatabaseSqliteDbPath); var configPreferencesDbPath = _cfg.GetCVar(CCVars.DatabaseSqliteDbPath);
var inMemory = _res.UserData.RootDir == null; var inMemory = _res.UserData.RootDir == null;
@@ -447,7 +447,7 @@ namespace Content.Server.Database
return builder.Options; return builder.Options;
} }
private void SetupLogging(DbContextOptionsBuilder<ServerDbContext> builder) private void SetupLogging(DbContextOptionsBuilder builder)
{ {
builder.UseLoggerFactory(_msLoggerFactory); builder.UseLoggerFactory(_msLoggerFactory);
} }

View File

@@ -13,10 +13,10 @@ namespace Content.Server.Database
{ {
public sealed class ServerDbPostgres : ServerDbBase public sealed class ServerDbPostgres : ServerDbBase
{ {
private readonly DbContextOptions<ServerDbContext> _options; private readonly DbContextOptions<PostgresServerDbContext> _options;
private readonly Task _dbReadyTask; private readonly Task _dbReadyTask;
public ServerDbPostgres(DbContextOptions<ServerDbContext> options) public ServerDbPostgres(DbContextOptions<PostgresServerDbContext> options)
{ {
_options = options; _options = options;

View File

@@ -31,7 +31,7 @@ namespace Content.Server.Database
private readonly Task _dbReadyTask; private readonly Task _dbReadyTask;
private readonly SqliteServerDbContext _prefsCtx; private readonly SqliteServerDbContext _prefsCtx;
public ServerDbSqlite(DbContextOptions<ServerDbContext> options) public ServerDbSqlite(DbContextOptions<SqliteServerDbContext> options)
{ {
_prefsCtx = new SqliteServerDbContext(options); _prefsCtx = new SqliteServerDbContext(options);

View File

@@ -67,7 +67,7 @@ namespace Content.Tests.Server.Preferences
private static ServerDbSqlite GetDb() private static ServerDbSqlite GetDb()
{ {
var builder = new DbContextOptionsBuilder<ServerDbContext>(); var builder = new DbContextOptionsBuilder<SqliteServerDbContext>();
var conn = new SqliteConnection("Data Source=:memory:"); var conn = new SqliteConnection("Data Source=:memory:");
conn.Open(); conn.Open();
builder.UseSqlite(conn); builder.UseSqlite(conn);