diff --git a/src/dbup-postgresql/DataSourceConnectionFactory.cs b/src/dbup-postgresql/DataSourceConnectionFactory.cs
new file mode 100644
index 0000000..56c0a12
--- /dev/null
+++ b/src/dbup-postgresql/DataSourceConnectionFactory.cs
@@ -0,0 +1,93 @@
+using System;
+using System.Data;
+using DbUp.Engine.Output;
+using DbUp.Engine.Transactions;
+using Npgsql;
+
+namespace DbUp.Postgresql;
+
+///
+/// A connection factory that uses Npgsql's data source pattern to create PostgreSQL database connections.
+/// This factory provides better performance and resource management compared to traditional connection strings
+/// by reusing configured data sources and connection pooling.
+///
+internal class DataSourceConnectionFactory : IConnectionFactory
+{
+
+ private readonly NpgsqlDataSource dataSource;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The PostgreSQL connection string used to configure the data source.
+ /// Additional connection options including SSL certificate configuration.
+ /// Thrown when or is null.
+ /// Thrown when is empty or invalid.
+ public DataSourceConnectionFactory(string connectionString, PostgresqlConnectionOptions connectionOptions)
+ {
+ if (connectionString == null)
+ {
+ throw new ArgumentNullException(nameof(connectionString));
+ }
+ if (string.IsNullOrEmpty(connectionString))
+ {
+ throw new ArgumentException("Connection string cannot be empty.", nameof(connectionString));
+ }
+ if (connectionOptions == null)
+ {
+ throw new ArgumentNullException(nameof(connectionOptions));
+ }
+ var builder = new NpgsqlDataSourceBuilder(connectionString);
+
+#if NET8_0_OR_GREATER
+ // Use the new SSL authentication callback API for .NET 8.0 with Npgsql 9
+ if (connectionOptions.ClientCertificate != null || connectionOptions.UserCertificateValidationCallback != null)
+ {
+ builder.UseSslClientAuthenticationOptionsCallback(options =>
+ {
+ if (connectionOptions.ClientCertificate != null)
+ {
+ options.ClientCertificates = new System.Security.Cryptography.X509Certificates.X509CertificateCollection
+ {
+ connectionOptions.ClientCertificate
+ };
+ }
+ if (connectionOptions.UserCertificateValidationCallback != null)
+ {
+ options.RemoteCertificateValidationCallback = connectionOptions.UserCertificateValidationCallback;
+ }
+ });
+ }
+#else
+ // Use legacy API for netstandard2.0 with Npgsql 8
+ if (connectionOptions.ClientCertificate != null)
+ {
+ builder.UseClientCertificate(connectionOptions.ClientCertificate);
+ }
+ if (connectionOptions.UserCertificateValidationCallback != null)
+ {
+ builder.UseUserCertificateValidationCallback(connectionOptions.UserCertificateValidationCallback);
+ }
+#endif
+ dataSource = builder.Build();
+ }
+
+ ///
+ /// Creates a new database connection using the configured data source.
+ ///
+ /// The upgrade log for recording connection-related messages. This parameter is not used in this implementation.
+ /// The database connection manager. This parameter is not used in this implementation.
+ /// A new instance ready for use.
+ ///
+ /// The returned connection is not automatically opened. The caller is responsible for opening and properly disposing of the connection.
+ /// The connection benefits from the data source's connection pooling and configuration reuse.
+ ///
+ public IDbConnection CreateConnection(IUpgradeLog upgradeLog, DatabaseConnectionManager databaseConnectionManager) => dataSource.CreateConnection();
+
+ ///
+ /// Creates a new database connection using the configured data source.
+ /// Simpler implementation of for internal use.
+ ///
+ /// A new instance ready for use.
+ internal NpgsqlConnection CreateConnection() => dataSource.CreateConnection();
+}
diff --git a/src/dbup-postgresql/PostgresqlConnectionManager.cs b/src/dbup-postgresql/PostgresqlConnectionManager.cs
index b30bea3..14eeea1 100644
--- a/src/dbup-postgresql/PostgresqlConnectionManager.cs
+++ b/src/dbup-postgresql/PostgresqlConnectionManager.cs
@@ -1,4 +1,5 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography.X509Certificates;
using DbUp.Engine.Transactions;
@@ -15,7 +16,7 @@ public class PostgresqlConnectionManager : DatabaseConnectionManager
/// Disallow single quotes to be escaped with a backslash (\')
///
public bool StandardConformingStrings { get; set; } = true;
-
+
///
/// Creates a new PostgreSQL database connection.
///
@@ -44,14 +45,7 @@ public PostgresqlConnectionManager(string connectionString, X509Certificate2 cer
/// The PostgreSQL connection string.
/// Custom options to apply on the created connection
public PostgresqlConnectionManager(string connectionString, PostgresqlConnectionOptions connectionOptions)
- : base(new DelegateConnectionFactory(l =>
- {
- NpgsqlConnection databaseConnection = new NpgsqlConnection(connectionString);
- databaseConnection.ApplyConnectionOptions(connectionOptions);
-
- return databaseConnection;
- }
- ))
+ : base(new DataSourceConnectionFactory(connectionString, connectionOptions))
{
}
@@ -78,4 +72,4 @@ public override IEnumerable SplitScriptIntoCommands(string scriptContent
return scriptStatements;
}
-}
\ No newline at end of file
+}
diff --git a/src/dbup-postgresql/PostgresqlExtensions.cs b/src/dbup-postgresql/PostgresqlExtensions.cs
index 5aef97b..f6ed796 100644
--- a/src/dbup-postgresql/PostgresqlExtensions.cs
+++ b/src/dbup-postgresql/PostgresqlExtensions.cs
@@ -194,42 +194,40 @@ PostgresqlConnectionOptions connectionOptions
logger.LogDebug("Master ConnectionString => {0}", logMasterConnectionStringBuilder.ConnectionString);
- using (var connection = new NpgsqlConnection(masterConnectionStringBuilder.ConnectionString))
+ var factory = new DataSourceConnectionFactory(masterConnectionStringBuilder.ConnectionString, connectionOptions);
+ using var connection = factory.CreateConnection();
+ connection.Open();
+
+ var sqlCommandText =
+ $"SELECT case WHEN oid IS NOT NULL THEN 1 ELSE 0 end FROM pg_database WHERE datname = '{databaseName}' limit 1;";
+
+ // check to see if the database already exists..
+ using (var command = new NpgsqlCommand(sqlCommandText, connection)
+ {
+ CommandType = CommandType.Text
+ })
{
- connection.ApplyConnectionOptions(connectionOptions);
- connection.Open();
+ var results = Convert.ToInt32(command.ExecuteScalar());
- var sqlCommandText =
- $@"SELECT case WHEN oid IS NOT NULL THEN 1 ELSE 0 end FROM pg_database WHERE datname = '{databaseName}' limit 1;";
-
- // check to see if the database already exists..
- using (var command = new NpgsqlCommand(sqlCommandText, connection)
- {
- CommandType = CommandType.Text
- })
+ // if the database exists, we're done here...
+ if (results == 1)
{
- var results = Convert.ToInt32(command.ExecuteScalar());
-
- // if the database exists, we're done here...
- if (results == 1)
- {
- return;
- }
+ return;
}
+ }
- sqlCommandText = $"create database \"{databaseName}\";";
-
- // Create the database...
- using (var command = new NpgsqlCommand(sqlCommandText, connection)
- {
- CommandType = CommandType.Text
- })
- {
- command.ExecuteNonQuery();
- }
+ sqlCommandText = $"create database \"{databaseName}\";";
- logger.LogInformation(@"Created database {0}", databaseName);
+ // Create the database...
+ using (var command = new NpgsqlCommand(sqlCommandText, connection)
+ {
+ CommandType = CommandType.Text
+ })
+ {
+ command.ExecuteNonQuery();
}
+
+ logger.LogInformation(@"Created database {0}", databaseName);
}
///
@@ -244,16 +242,4 @@ public static UpgradeEngineBuilder JournalToPostgresqlTable(this UpgradeEngineBu
builder.Configure(c => c.Journal = new PostgresqlTableJournal(() => c.ConnectionManager, () => c.Log, schema, table));
return builder;
}
-
- internal static void ApplyConnectionOptions(this NpgsqlConnection connection, PostgresqlConnectionOptions connectionOptions)
- {
- connection.SslClientAuthenticationOptionsCallback = options =>
- {
- if (connectionOptions?.ClientCertificate != null)
- options.ClientCertificates = new X509Certificate2Collection(connectionOptions.ClientCertificate);
-
- if (connectionOptions?.UserCertificateValidationCallback != null)
- options.RemoteCertificateValidationCallback = connectionOptions.UserCertificateValidationCallback;
- };
- }
}
diff --git a/src/dbup-postgresql/dbup-postgresql.csproj b/src/dbup-postgresql/dbup-postgresql.csproj
index f121447..e6df8c2 100644
--- a/src/dbup-postgresql/dbup-postgresql.csproj
+++ b/src/dbup-postgresql/dbup-postgresql.csproj
@@ -6,7 +6,7 @@
DbUp Contributors
DbUp
Copyright © DbUp Contributors 2015
- net8.0
+ netstandard2.0;net8.0
dbup-postgresql
DbUp.Postgresql
dbup-postgresql
@@ -24,7 +24,14 @@
-
+
+
+
+
+
+
+
+