diff --git a/src/Weasel.CommandLine.Tests/NamedTable.cs b/src/Weasel.CommandLine.Tests/NamedTable.cs index 9c0bf826..3bbeab45 100644 --- a/src/Weasel.CommandLine.Tests/NamedTable.cs +++ b/src/Weasel.CommandLine.Tests/NamedTable.cs @@ -1,4 +1,5 @@ using JasperFx.Core; +using Npgsql; using Weasel.Core; using Weasel.Core.Migrations; using Weasel.Postgresql; @@ -39,7 +40,7 @@ protected override IEnumerable schemaObjects() public class DatabaseWithTables: PostgresqlDatabase { public DatabaseWithTables(AutoCreate autoCreate, string identifier) : - base(new DefaultMigrationLogger(), autoCreate, new PostgresqlMigrator(), identifier, ConnectionSource.ConnectionString) + base(new DefaultMigrationLogger(), autoCreate, new PostgresqlMigrator(), identifier, new NpgsqlDataSourceBuilder(ConnectionSource.ConnectionString).Build()) { } diff --git a/src/Weasel.Postgresql.Tests/Migrations/creating_and_dropping_databases.cs b/src/Weasel.Postgresql.Tests/Migrations/creating_and_dropping_databases.cs index 90f8f85e..159defe5 100644 --- a/src/Weasel.Postgresql.Tests/Migrations/creating_and_dropping_databases.cs +++ b/src/Weasel.Postgresql.Tests/Migrations/creating_and_dropping_databases.cs @@ -6,6 +6,7 @@ using System; using Shouldly; using Weasel.Core; +using Weasel.Postgresql.Connections; namespace Weasel.Postgresql.Tests.Migrations; @@ -35,13 +36,13 @@ public async Task can_build_databases_once() public class Databases: SingleServerDatabaseCollection { - public Databases() : base(ConnectionSource.ConnectionString) + public Databases() : base(new DefaultNpgsqlDataSourceFactory(), ConnectionSource.ConnectionString) { } - protected override DatabaseWithTables buildDatabase(string databaseName, string connectionString) + protected override DatabaseWithTables buildDatabase(string databaseName, NpgsqlDataSource dataSource) { - return new DatabaseWithTables(databaseName, connectionString); + return new DatabaseWithTables(databaseName, dataSource); } } } diff --git a/src/Weasel.Postgresql.Tests/Migrations/migration_scenario_tests.cs b/src/Weasel.Postgresql.Tests/Migrations/migration_scenario_tests.cs index aacd52ae..f50b1cad 100644 --- a/src/Weasel.Postgresql.Tests/Migrations/migration_scenario_tests.cs +++ b/src/Weasel.Postgresql.Tests/Migrations/migration_scenario_tests.cs @@ -20,7 +20,7 @@ public class SchemaMigrationTests : IntegrationContext, IAsyncLifetime public SchemaMigrationTests() : base("migrations") { - theDatabase = new DatabaseWithTables(AutoCreate.None, "Migrations"); + theDatabase = new DatabaseWithTables(AutoCreate.None, "Migrations", theDataSource); } public override Task InitializeAsync() @@ -154,31 +154,26 @@ public class DatabaseWithTables: PostgresqlDatabase public static DatabaseWithTables ForDataSource(NpgsqlDataSource dataSource) { var builder = new NpgsqlConnectionStringBuilder(dataSource.ConnectionString); - var identifier = builder.Database; + var identifier = builder.Database!; return new DatabaseWithTables(identifier, dataSource); } public static DatabaseWithTables ForConnectionString(string connectionString) { - var builder = new NpgsqlConnectionStringBuilder(connectionString); - var identifier = builder.Database; + var builder = new NpgsqlDataSourceBuilder(connectionString); + var identifier = builder.ConnectionStringBuilder.Database!; - return new DatabaseWithTables(identifier, connectionString); + return new DatabaseWithTables(identifier, builder.Build()); } - public DatabaseWithTables(AutoCreate autoCreate, string identifier) - : base(new DefaultMigrationLogger(), autoCreate, new PostgresqlMigrator(), identifier, ConnectionSource.ConnectionString) - { - } - - public DatabaseWithTables(string identifier, string connectionString) - : base(new DefaultMigrationLogger(), AutoCreate.All, new PostgresqlMigrator(), identifier, connectionString) + public DatabaseWithTables(string identifier, NpgsqlDataSource dataSource) + : base(new DefaultMigrationLogger(), AutoCreate.All, new PostgresqlMigrator(), identifier, dataSource) { } - public DatabaseWithTables(string identifier, NpgsqlDataSource dataSource) - : base(new DefaultMigrationLogger(), AutoCreate.All, new PostgresqlMigrator(), identifier, dataSource) + public DatabaseWithTables(AutoCreate autoCreate, string identifier, NpgsqlDataSource dataSource) + : base(new DefaultMigrationLogger(), autoCreate, new PostgresqlMigrator(), identifier, dataSource) { } diff --git a/src/Weasel.Postgresql/Connections/DefaultNpgsqlDataSourceFactory.cs b/src/Weasel.Postgresql/Connections/DefaultNpgsqlDataSourceFactory.cs new file mode 100644 index 00000000..0b13b7be --- /dev/null +++ b/src/Weasel.Postgresql/Connections/DefaultNpgsqlDataSourceFactory.cs @@ -0,0 +1,35 @@ +using JasperFx.Core; +using Npgsql; + +namespace Weasel.Postgresql.Connections; + +public class DefaultNpgsqlDataSourceFactory: INpgsqlDataSourceFactory +{ + private readonly Cache builderCache = new(); + + public DefaultNpgsqlDataSourceFactory(Func dataSourceBuilderFactory) + { + builderCache.OnMissing = dataSourceBuilderFactory; + } + + public DefaultNpgsqlDataSourceFactory(): this(connectionString => new NpgsqlDataSourceBuilder(connectionString)) + { + } + + public NpgsqlDataSource Create(string connectionString) + { + var builder = builderCache[connectionString]; + + return builder.Build(); + } + + public NpgsqlDataSource Create(string masterConnectionString, string databaseName) + { + var connectionString = new NpgsqlConnectionStringBuilder(masterConnectionString) { Database = databaseName } + .ConnectionString; + + var builder = builderCache[connectionString]; + + return builder.Build(); + } +} diff --git a/src/Weasel.Postgresql/Connections/INpgsqlDataSourceFactory.cs b/src/Weasel.Postgresql/Connections/INpgsqlDataSourceFactory.cs new file mode 100644 index 00000000..6d6b0f8f --- /dev/null +++ b/src/Weasel.Postgresql/Connections/INpgsqlDataSourceFactory.cs @@ -0,0 +1,11 @@ +using Npgsql; + +namespace Weasel.Postgresql.Connections; + +public interface INpgsqlDataSourceFactory +{ + NpgsqlDataSource Create(string connectionString); + + + NpgsqlDataSource Create(string masterConnectionString, string databaseName); +} diff --git a/src/Weasel.Postgresql/Migrations/SingleServerDatabaseCollection.cs b/src/Weasel.Postgresql/Migrations/SingleServerDatabaseCollection.cs index 519b19e5..52fb342f 100644 --- a/src/Weasel.Postgresql/Migrations/SingleServerDatabaseCollection.cs +++ b/src/Weasel.Postgresql/Migrations/SingleServerDatabaseCollection.cs @@ -1,6 +1,7 @@ using JasperFx.Core; using Npgsql; using Weasel.Core.Migrations; +using Weasel.Postgresql.Connections; namespace Weasel.Postgresql.Migrations; @@ -11,23 +12,25 @@ namespace Weasel.Postgresql.Migrations; /// public abstract class SingleServerDatabaseCollection where T : PostgresqlDatabase { - private readonly NpgsqlDataSource? npgsqlDataSource; + private readonly INpgsqlDataSourceFactory dataSourceFactory; + private readonly NpgsqlDataSource masterDataSource; private readonly TimedLock _lock = new(); - private readonly string _masterConnectionString; - private ImHashMap _databases = ImHashMap.Empty; + private ImHashMap databases = ImHashMap.Empty; - protected SingleServerDatabaseCollection(NpgsqlDataSource npgsqlDataSource) + protected SingleServerDatabaseCollection(INpgsqlDataSourceFactory dataSourceFactory, + NpgsqlDataSource masterDataSource) { - this.npgsqlDataSource = npgsqlDataSource; - _masterConnectionString = npgsqlDataSource.ConnectionString; + this.dataSourceFactory = dataSourceFactory; + this.masterDataSource = masterDataSource; } - protected SingleServerDatabaseCollection(string masterConnectionString) + protected SingleServerDatabaseCollection(INpgsqlDataSourceFactory dataSourceFactory, string masterConnectionString) + : this(dataSourceFactory, dataSourceFactory.Create(masterConnectionString)) { - _masterConnectionString = masterConnectionString; } - private DatabaseSpecification Specification { get; } = new(); + static + private DatabaseSpecification Specification { get; } = new(); /// /// Force the database to be dropped and re-created @@ -36,28 +39,26 @@ protected SingleServerDatabaseCollection(string masterConnectionString) public IReadOnlyList AllDatabases() { - return _databases.Enumerate().Select(x => x.Value).ToList(); + return databases.Enumerate().Select(x => x.Value).ToList(); } - protected abstract T buildDatabase(string databaseName, string connectionString); + protected abstract T buildDatabase(string databaseName, NpgsqlDataSource dataSource); public virtual async ValueTask FindOrCreateDatabase(string databaseName, CancellationToken ct = default) { - if (_databases.TryFind(databaseName, out var database)) + if (databases.TryFind(databaseName, out var database)) { return database; } using (await _lock.Lock(5.Seconds(), ct).ConfigureAwait(false)) { - if (_databases.TryFind(databaseName, out database)) + if (databases.TryFind(databaseName, out database)) { return database; } - await using var conn = - npgsqlDataSource?.CreateConnection() - ?? new NpgsqlConnection(_masterConnectionString); + await using var conn = masterDataSource.CreateConnection(); await conn.OpenAsync(ct).ConfigureAwait(false); if (DropAndRecreate) @@ -70,12 +71,12 @@ public virtual async ValueTask FindOrCreateDatabase(string databaseName, Canc await Specification.BuildDatabase(conn, databaseName, ct).ConfigureAwait(false); } - var builder = new NpgsqlConnectionStringBuilder(_masterConnectionString) { Database = databaseName }; + database = buildDatabase( + databaseName, + dataSourceFactory.Create(masterDataSource.ConnectionString, databaseName) + ); - var connectionString = builder.ConnectionString; - database = buildDatabase(databaseName, connectionString); - - _databases = _databases.AddOrUpdate(databaseName, database); + databases = databases.AddOrUpdate(databaseName, database); return database; } diff --git a/src/Weasel.Postgresql/PostgresqlDatabase.cs b/src/Weasel.Postgresql/PostgresqlDatabase.cs index 2c1ed457..4be5d39c 100644 --- a/src/Weasel.Postgresql/PostgresqlDatabase.cs +++ b/src/Weasel.Postgresql/PostgresqlDatabase.cs @@ -7,16 +7,6 @@ namespace Weasel.Postgresql; public abstract class PostgresqlDatabase: DatabaseBase { - protected PostgresqlDatabase( - IMigrationLogger logger, - AutoCreate autoCreate, - PostgresqlMigrator migrator, - string identifier, - string connectionString - ): base(logger, autoCreate, migrator, identifier, connectionString) - { - } - protected PostgresqlDatabase( IMigrationLogger logger, AutoCreate autoCreate, @@ -27,16 +17,6 @@ NpgsqlDataSource dataSource { } - protected PostgresqlDatabase( - IMigrationLogger logger, - AutoCreate autoCreate, - PostgresqlMigrator migrator, - string identifier, - Func connectionSource - ): base(logger, autoCreate, migrator, identifier, connectionSource) - { - } - public async Task DefinitionForFunction(DbObjectName function, CancellationToken ct = default) { await using var conn = CreateConnection();