From ea4ad02f15205823903465c887467d81314d9056 Mon Sep 17 00:00:00 2001 From: Rick van Dam Date: Fri, 5 Apr 2024 13:33:17 +0200 Subject: [PATCH] use hashed createscript as a stable name for the database --- .../BloggingContext.cs | 9 ++++-- .../AngularAuth.Backend.Sut/Program.cs | 5 +-- .../MsSql/Api.MsSql.Sut/BloggingContext.cs | 11 ++++--- Examples/Api/MsSql/Api.MsSql.Sut/Program.cs | 5 +-- .../Api.PostgreSql.Sut/BloggingContext.cs | 9 ++++-- .../PostgreSql/Api.PostgreSql.Sut/Program.cs | 5 +-- .../ApiJwtAuth.Sut/BloggingContext.cs | 9 ++++-- Examples/ApiJwtAuth/ApiJwtAuth.Sut/Program.cs | 5 +-- .../MigrationTests.cs | 2 +- .../BloggingContext.cs | 11 ++++--- .../Program.cs | 5 +-- Examples/Razor/Razor.Sut/BloggingContext.cs | 9 ++++-- Examples/Razor/Razor.Sut/Program.cs | 5 +-- .../Vue/Vue.Backend.Sut/BloggingContext.cs | 9 ++++-- Examples/Vue/Vue.Backend.Sut/Program.cs | 5 +-- .../DataBaseNameGenerator.cs | 17 ---------- .../DbContextMigrationInitializer.cs | 31 +++++++++++++++++-- .../ServiceCollectionExtensions.cs | 5 ++- TestExamplesDotnet.Mssql/MsSqlDatabase.cs | 2 +- .../MsSqlDatabasePoolPolicy.cs | 1 - .../PostgreSqlDatabase.cs | 4 +-- .../PostgreSqlDatabasePoolPolicy.cs | 7 ++--- TestExamplesDotnet/IDataBaseNameGenerator.cs | 6 ---- TestExamplesDotnet/IDatabaseInitializer.cs | 1 + 24 files changed, 94 insertions(+), 84 deletions(-) delete mode 100644 TestExamplesDotnet.EntityFrameworkCore/DataBaseNameGenerator.cs delete mode 100644 TestExamplesDotnet/IDataBaseNameGenerator.cs diff --git a/Examples/AngularAuth/AngularAuth.Backend.Sut/BloggingContext.cs b/Examples/AngularAuth/AngularAuth.Backend.Sut/BloggingContext.cs index 999661c..f9a9f52 100644 --- a/Examples/AngularAuth/AngularAuth.Backend.Sut/BloggingContext.cs +++ b/Examples/AngularAuth/AngularAuth.Backend.Sut/BloggingContext.cs @@ -4,15 +4,20 @@ namespace AngularAuth.Backend.Sut; public class BloggingContext : DbContext { + private readonly string? _connectionString; public DbSet Blogs => Set(); public DbSet Posts => Set(); - public BloggingContext() + public BloggingContext() { } + + public BloggingContext(IConfiguration configuration) { + _connectionString = configuration["DbConnectionString"]; } - public BloggingContext(DbContextOptions context) : base(context) + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { + optionsBuilder.UseNpgsql(_connectionString); } } diff --git a/Examples/AngularAuth/AngularAuth.Backend.Sut/Program.cs b/Examples/AngularAuth/AngularAuth.Backend.Sut/Program.cs index 4c6584d..9fb953d 100644 --- a/Examples/AngularAuth/AngularAuth.Backend.Sut/Program.cs +++ b/Examples/AngularAuth/AngularAuth.Backend.Sut/Program.cs @@ -8,10 +8,7 @@ WebRootPath = "wwwroot/browser" }); -builder.Services.AddDbContext(options => -{ - options.UseNpgsql(builder.Configuration["DbConnectionString"]); -}); +builder.Services.AddDbContext(); builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(JwtBearerDefaults.AuthenticationScheme); diff --git a/Examples/Api/MsSql/Api.MsSql.Sut/BloggingContext.cs b/Examples/Api/MsSql/Api.MsSql.Sut/BloggingContext.cs index d2d0228..da268c2 100644 --- a/Examples/Api/MsSql/Api.MsSql.Sut/BloggingContext.cs +++ b/Examples/Api/MsSql/Api.MsSql.Sut/BloggingContext.cs @@ -4,17 +4,20 @@ namespace Api.MsSql.Sut; public class BloggingContext : DbContext { + private readonly string? _connectionString; public DbSet Blogs => Set(); public DbSet Posts => Set(); - public BloggingContext() - { + public BloggingContext() { } + public BloggingContext(IConfiguration configuration) + { + _connectionString = configuration["DbConnectionString"]; } - public BloggingContext(DbContextOptions context) : base(context) + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { - + optionsBuilder.UseSqlServer(_connectionString); } } diff --git a/Examples/Api/MsSql/Api.MsSql.Sut/Program.cs b/Examples/Api/MsSql/Api.MsSql.Sut/Program.cs index af8b423..78ea3d5 100644 --- a/Examples/Api/MsSql/Api.MsSql.Sut/Program.cs +++ b/Examples/Api/MsSql/Api.MsSql.Sut/Program.cs @@ -3,10 +3,7 @@ var builder = WebApplication.CreateBuilder(args); -builder.Services.AddDbContext(options => -{ - options.UseSqlServer(builder.Configuration["DbConnectionString"]); -}); +builder.Services.AddDbContext(); var app = builder.Build(); diff --git a/Examples/Api/PostgreSql/Api.PostgreSql.Sut/BloggingContext.cs b/Examples/Api/PostgreSql/Api.PostgreSql.Sut/BloggingContext.cs index fd9e56e..633d82f 100644 --- a/Examples/Api/PostgreSql/Api.PostgreSql.Sut/BloggingContext.cs +++ b/Examples/Api/PostgreSql/Api.PostgreSql.Sut/BloggingContext.cs @@ -4,15 +4,20 @@ namespace Api.PostgreSql.Sut; public class BloggingContext : DbContext { + private readonly string? _connectionString; public DbSet Blogs => Set(); public DbSet Posts => Set(); - public BloggingContext() + public BloggingContext() { } + + public BloggingContext(IConfiguration configuration) { + _connectionString = configuration["DbConnectionString"]; } - public BloggingContext(DbContextOptions context) : base(context) + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { + optionsBuilder.UseNpgsql(_connectionString); } } diff --git a/Examples/Api/PostgreSql/Api.PostgreSql.Sut/Program.cs b/Examples/Api/PostgreSql/Api.PostgreSql.Sut/Program.cs index c96b8a8..c7a3718 100644 --- a/Examples/Api/PostgreSql/Api.PostgreSql.Sut/Program.cs +++ b/Examples/Api/PostgreSql/Api.PostgreSql.Sut/Program.cs @@ -3,10 +3,7 @@ var builder = WebApplication.CreateBuilder(args); -builder.Services.AddDbContext(options => -{ - options.UseNpgsql(builder.Configuration["DbConnectionString"]); -}); +builder.Services.AddDbContext(); var app = builder.Build(); diff --git a/Examples/ApiJwtAuth/ApiJwtAuth.Sut/BloggingContext.cs b/Examples/ApiJwtAuth/ApiJwtAuth.Sut/BloggingContext.cs index 41d755b..ae7ea2d 100644 --- a/Examples/ApiJwtAuth/ApiJwtAuth.Sut/BloggingContext.cs +++ b/Examples/ApiJwtAuth/ApiJwtAuth.Sut/BloggingContext.cs @@ -4,15 +4,20 @@ namespace ApiJwtAuth.Sut; public class BloggingContext : DbContext { + private readonly string? _connectionString; public DbSet Blogs => Set(); public DbSet Posts => Set(); - public BloggingContext() + public BloggingContext() { } + + public BloggingContext(IConfiguration configuration) { + _connectionString = configuration["DbConnectionString"]; } - public BloggingContext(DbContextOptions context) : base(context) + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { + optionsBuilder.UseNpgsql(_connectionString); } } diff --git a/Examples/ApiJwtAuth/ApiJwtAuth.Sut/Program.cs b/Examples/ApiJwtAuth/ApiJwtAuth.Sut/Program.cs index 344e2de..51e356e 100644 --- a/Examples/ApiJwtAuth/ApiJwtAuth.Sut/Program.cs +++ b/Examples/ApiJwtAuth/ApiJwtAuth.Sut/Program.cs @@ -10,10 +10,7 @@ builder.Services.AddAuthorization(); -builder.Services.AddDbContext(options => -{ - options.UseNpgsql(builder.Configuration["DbConnectionString"]); -}); +builder.Services.AddDbContext(); var app = builder.Build(); diff --git a/Examples/Migrations.MsSql.EntityFrameworkCore/Migrations.MsSql.EntityFrameworkCore.Nunit/MigrationTests.cs b/Examples/Migrations.MsSql.EntityFrameworkCore/Migrations.MsSql.EntityFrameworkCore.Nunit/MigrationTests.cs index 9f69fc9..333b610 100644 --- a/Examples/Migrations.MsSql.EntityFrameworkCore/Migrations.MsSql.EntityFrameworkCore.Nunit/MigrationTests.cs +++ b/Examples/Migrations.MsSql.EntityFrameworkCore/Migrations.MsSql.EntityFrameworkCore.Nunit/MigrationTests.cs @@ -49,7 +49,7 @@ private sealed class MigrationTestCases : IEnumerable { public IEnumerator GetEnumerator() { - using DbContext context = new BloggingContext(new DbContextOptionsBuilder().UseSqlServer().Options); + using DbContext context = new BloggingContext(); var migrations = context.GenerateMigrationScripts(); foreach (var migration in migrations) diff --git a/Examples/Migrations.MsSql.EntityFrameworkCore/Migrations.MsSql.EntityFrameworkCore.Sut/BloggingContext.cs b/Examples/Migrations.MsSql.EntityFrameworkCore/Migrations.MsSql.EntityFrameworkCore.Sut/BloggingContext.cs index 536d9d1..7b249af 100644 --- a/Examples/Migrations.MsSql.EntityFrameworkCore/Migrations.MsSql.EntityFrameworkCore.Sut/BloggingContext.cs +++ b/Examples/Migrations.MsSql.EntityFrameworkCore/Migrations.MsSql.EntityFrameworkCore.Sut/BloggingContext.cs @@ -4,17 +4,20 @@ namespace Migrations.MsSql.EntityFrameworkCore.Sut; public class BloggingContext : DbContext { + private readonly string? _connectionString; public DbSet Blogs => Set(); public DbSet Posts => Set(); - public BloggingContext() - { + public BloggingContext() { } + public BloggingContext(IConfiguration configuration) + { + _connectionString = configuration["DbConnectionString"]; } - public BloggingContext(DbContextOptions context) : base(context) + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { - + optionsBuilder.UseSqlServer(_connectionString); } } diff --git a/Examples/Migrations.MsSql.EntityFrameworkCore/Migrations.MsSql.EntityFrameworkCore.Sut/Program.cs b/Examples/Migrations.MsSql.EntityFrameworkCore/Migrations.MsSql.EntityFrameworkCore.Sut/Program.cs index a5edd74..5f2d9d7 100644 --- a/Examples/Migrations.MsSql.EntityFrameworkCore/Migrations.MsSql.EntityFrameworkCore.Sut/Program.cs +++ b/Examples/Migrations.MsSql.EntityFrameworkCore/Migrations.MsSql.EntityFrameworkCore.Sut/Program.cs @@ -3,10 +3,7 @@ var builder = WebApplication.CreateBuilder(args); -builder.Services.AddDbContext(options => -{ - options.UseSqlServer(builder.Configuration["DbConnectionString"]); -}); +builder.Services.AddDbContext(); var app = builder.Build(); diff --git a/Examples/Razor/Razor.Sut/BloggingContext.cs b/Examples/Razor/Razor.Sut/BloggingContext.cs index e890fab..43d333b 100644 --- a/Examples/Razor/Razor.Sut/BloggingContext.cs +++ b/Examples/Razor/Razor.Sut/BloggingContext.cs @@ -4,15 +4,20 @@ namespace Razor.PostgreSql.Sut; public class BloggingContext : DbContext { + private readonly string? _connectionString; public DbSet Blogs => Set(); public DbSet Posts => Set(); - public BloggingContext() + public BloggingContext() { } + + public BloggingContext(IConfiguration configuration) { + _connectionString = configuration["DbConnectionString"]; } - public BloggingContext(DbContextOptions context) : base(context) + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { + optionsBuilder.UseNpgsql(_connectionString); } } diff --git a/Examples/Razor/Razor.Sut/Program.cs b/Examples/Razor/Razor.Sut/Program.cs index 545039f..395dc1e 100644 --- a/Examples/Razor/Razor.Sut/Program.cs +++ b/Examples/Razor/Razor.Sut/Program.cs @@ -3,10 +3,7 @@ var builder = WebApplication.CreateBuilder(args); -builder.Services.AddDbContext(options => -{ - options.UseNpgsql(builder.Configuration["DbConnectionString"]); -}); +builder.Services.AddDbContext(); builder.Services.AddRazorPages(); diff --git a/Examples/Vue/Vue.Backend.Sut/BloggingContext.cs b/Examples/Vue/Vue.Backend.Sut/BloggingContext.cs index 43e2b54..e422878 100644 --- a/Examples/Vue/Vue.Backend.Sut/BloggingContext.cs +++ b/Examples/Vue/Vue.Backend.Sut/BloggingContext.cs @@ -4,15 +4,20 @@ namespace Vue.Backend.Sut; public class BloggingContext : DbContext { + private readonly string? _connectionString; public DbSet Blogs => Set(); public DbSet Posts => Set(); - public BloggingContext() + public BloggingContext() { } + + public BloggingContext(IConfiguration configuration) { + _connectionString = configuration["DbConnectionString"]; } - public BloggingContext(DbContextOptions context) : base(context) + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { + optionsBuilder.UseNpgsql(_connectionString); } } diff --git a/Examples/Vue/Vue.Backend.Sut/Program.cs b/Examples/Vue/Vue.Backend.Sut/Program.cs index d2ed8e7..d5c98c7 100644 --- a/Examples/Vue/Vue.Backend.Sut/Program.cs +++ b/Examples/Vue/Vue.Backend.Sut/Program.cs @@ -3,10 +3,7 @@ var builder = WebApplication.CreateBuilder(args); -builder.Services.AddDbContext(options => -{ - options.UseNpgsql(builder.Configuration["DbConnectionString"]); -}); +builder.Services.AddDbContext(); var app = builder.Build(); diff --git a/TestExamplesDotnet.EntityFrameworkCore/DataBaseNameGenerator.cs b/TestExamplesDotnet.EntityFrameworkCore/DataBaseNameGenerator.cs deleted file mode 100644 index d600102..0000000 --- a/TestExamplesDotnet.EntityFrameworkCore/DataBaseNameGenerator.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Microsoft.EntityFrameworkCore; - -namespace TestExamplesDotnet; - -public sealed class DataBaseNameGenerator : IDataBaseNameGenerator - where TDbContext : DbContext -{ - private int _counter; - - public string GetDataBaseName() - { - var migrationId = typeof(TDbContext).Assembly.ManifestModule.ModuleVersionId.ToString("N");; - - var counter = Interlocked.Increment(ref _counter); - return $"{migrationId}_{counter}"; - } -} diff --git a/TestExamplesDotnet.EntityFrameworkCore/DbContextMigrationInitializer.cs b/TestExamplesDotnet.EntityFrameworkCore/DbContextMigrationInitializer.cs index 9084b01..0a0aced 100644 --- a/TestExamplesDotnet.EntityFrameworkCore/DbContextMigrationInitializer.cs +++ b/TestExamplesDotnet.EntityFrameworkCore/DbContextMigrationInitializer.cs @@ -1,14 +1,35 @@ -using Microsoft.EntityFrameworkCore; +using System.Reflection; +using System.Security.Cryptography; +using System.Text; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; + namespace TestExamplesDotnet; public sealed class DbContextMigrationInitializer : IDatabaseInitializer - where TDbContext : DbContext + where TDbContext : DbContext, new() { + private int _counter; + private readonly Lazy _databaseHash; + + public DbContextMigrationInitializer() + { + _databaseHash = new Lazy(() => + { + using var context = new TDbContext(); + var createScript = context.GetService().GenerateCreateScript(); +#pragma warning disable CA5351 + return Convert.ToHexString(MD5.HashData(Encoding.UTF8.GetBytes(createScript))); +#pragma warning restore CA5351 + }); + } + public void Initialize(IHost app) { using var serviceScope = app.Services.GetRequiredService().CreateScope(); @@ -21,4 +42,10 @@ public void Initialize(IHost app) configurationRoot["Logging:LogLevel:Microsoft.EntityFrameworkCore"] = LogLevel.Information.ToString(); configurationRoot.Reload(); } + + public string GetUniqueDataBaseName() + { + var counter = Interlocked.Increment(ref _counter); + return $"{_databaseHash.Value}_{counter}"; + } } diff --git a/TestExamplesDotnet.EntityFrameworkCore/ServiceCollectionExtensions.cs b/TestExamplesDotnet.EntityFrameworkCore/ServiceCollectionExtensions.cs index 0fad172..25ebea8 100644 --- a/TestExamplesDotnet.EntityFrameworkCore/ServiceCollectionExtensions.cs +++ b/TestExamplesDotnet.EntityFrameworkCore/ServiceCollectionExtensions.cs @@ -6,9 +6,8 @@ namespace TestExamplesDotnet.EntityFrameworkCore; public static class ServiceCollectionExtensions { public static void RegisterMigrationInitializer(this IServiceCollection services) - where TContext : DbContext + where TContext : DbContext, new() { - services.AddTransient>(); - services.AddSingleton>(); + services.AddSingleton>(); } } diff --git a/TestExamplesDotnet.Mssql/MsSqlDatabase.cs b/TestExamplesDotnet.Mssql/MsSqlDatabase.cs index 7809c35..5ef24c2 100644 --- a/TestExamplesDotnet.Mssql/MsSqlDatabase.cs +++ b/TestExamplesDotnet.Mssql/MsSqlDatabase.cs @@ -18,7 +18,7 @@ public MsSqlDatabase(MsSqlContainer container, IDatabaseInitializer databaseInit { _databaseInitializer = databaseInitializer; _respawnerOptions = respawnerOptions; - ConnectionString = $"Server=127.0.0.1,{container.GetMappedPublicPort(1433)};Database={Guid.NewGuid()};User Id=sa;Password=yourStrong(!)Password;TrustServerCertificate=True"; + ConnectionString = $"Server=127.0.0.1,{container.GetMappedPublicPort(1433)};Database={databaseInitializer.GetUniqueDataBaseName()};User Id=sa;Password=yourStrong(!)Password;TrustServerCertificate=True"; } public void Initialize(IHost host) diff --git a/TestExamplesDotnet.Mssql/MsSqlDatabasePoolPolicy.cs b/TestExamplesDotnet.Mssql/MsSqlDatabasePoolPolicy.cs index 51b9912..cdcc8bd 100644 --- a/TestExamplesDotnet.Mssql/MsSqlDatabasePoolPolicy.cs +++ b/TestExamplesDotnet.Mssql/MsSqlDatabasePoolPolicy.cs @@ -10,7 +10,6 @@ public sealed class MsSqlDatabasePoolPolicy : IPooledObjectPolicy private readonly IDatabaseInitializer _databaseInitializer; private readonly RespawnerOptions _respawnerOptions; - public MsSqlDatabasePoolPolicy(MsSqlContainer container, IDatabaseInitializer databaseInitializer, RespawnerOptions respawnerOptions) { _container = container; diff --git a/TestExamplesDotnet.PostgreSql/PostgreSqlDatabase.cs b/TestExamplesDotnet.PostgreSql/PostgreSqlDatabase.cs index e310b31..0982266 100644 --- a/TestExamplesDotnet.PostgreSql/PostgreSqlDatabase.cs +++ b/TestExamplesDotnet.PostgreSql/PostgreSqlDatabase.cs @@ -13,11 +13,11 @@ public sealed class PostgreSqlDatabase : IDatabase private bool _initialized; public string ConnectionString { get; } - public PostgreSqlDatabase(PostgreSqlContainer container, IDatabaseInitializer databaseInitializer, RespawnerOptions respawnerOptions, IDataBaseNameGenerator dataBaseNameGenerator) + public PostgreSqlDatabase(PostgreSqlContainer container, IDatabaseInitializer databaseInitializer, RespawnerOptions respawnerOptions) { _databaseInitializer = databaseInitializer; _respawnerOptions = respawnerOptions; - ConnectionString = $"Host=127.0.0.1;Port={container.GetMappedPublicPort(5432)};Database={dataBaseNameGenerator.GetDataBaseName()};Username=postgres;Password=postgres;Include Error Detail=true"; + ConnectionString = $"Host=127.0.0.1;Port={container.GetMappedPublicPort(5432)};Database={databaseInitializer.GetUniqueDataBaseName()};Username=postgres;Password=postgres;Include Error Detail=true"; } public void Initialize(IHost host) diff --git a/TestExamplesDotnet.PostgreSql/PostgreSqlDatabasePoolPolicy.cs b/TestExamplesDotnet.PostgreSql/PostgreSqlDatabasePoolPolicy.cs index 8c73816..6b3b27a 100644 --- a/TestExamplesDotnet.PostgreSql/PostgreSqlDatabasePoolPolicy.cs +++ b/TestExamplesDotnet.PostgreSql/PostgreSqlDatabasePoolPolicy.cs @@ -9,18 +9,15 @@ public sealed class PostgreSqlDatabasePoolPolicy : IPooledObjectPolicy new PostgreSqlDatabase(_container, _databaseInitializer, _respawnerOptions, _dataBaseNameGenerator); + public IDatabase Create() => new PostgreSqlDatabase(_container, _databaseInitializer, _respawnerOptions); public bool Return(IDatabase obj) => true; } diff --git a/TestExamplesDotnet/IDataBaseNameGenerator.cs b/TestExamplesDotnet/IDataBaseNameGenerator.cs deleted file mode 100644 index 29f3e4f..0000000 --- a/TestExamplesDotnet/IDataBaseNameGenerator.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace TestExamplesDotnet; - -public interface IDataBaseNameGenerator -{ - string GetDataBaseName(); -} diff --git a/TestExamplesDotnet/IDatabaseInitializer.cs b/TestExamplesDotnet/IDatabaseInitializer.cs index a40c610..879f955 100644 --- a/TestExamplesDotnet/IDatabaseInitializer.cs +++ b/TestExamplesDotnet/IDatabaseInitializer.cs @@ -5,4 +5,5 @@ namespace TestExamplesDotnet; public interface IDatabaseInitializer { void Initialize(IHost app); + string GetUniqueDataBaseName(); }