diff --git a/pgLabII.PgUtils/ConnectionStrings/Abstractions.cs b/pgLabII.PgUtils/ConnectionStrings/Abstractions.cs
deleted file mode 100644
index 178d257..0000000
--- a/pgLabII.PgUtils/ConnectionStrings/Abstractions.cs
+++ /dev/null
@@ -1,76 +0,0 @@
-using System.Collections.Generic;
-using FluentResults;
-using Npgsql;
-
-namespace pgLabII.PgUtils.ConnectionStrings;
-
-public enum ConnStringFormat
-{
- Libpq,
- Npgsql,
- Url,
- Jdbc
-}
-
-public sealed class HostEndpoint
-{
- public string Host { get; init; } = string.Empty;
- public ushort? Port { get; init; }
-}
-
-///
-/// Canonical, format-agnostic representation of a PostgreSQL connection.
-/// Keep minimal fields for broad interoperability; store extras in Properties.
-///
-public sealed class ConnectionDescriptor
-{
- public string? Name { get; init; }
-
- // Primary hosts (support multi-host). If empty, implies localhost default.
- public IReadOnlyList Hosts { get; init; } = new List();
-
- public string? Database { get; init; }
- public string? Username { get; init; }
- public string? Password { get; init; }
-
- public SslMode? SslMode { get; init; }
-
- // Common optional fields
- public string? ApplicationName { get; init; }
- public int? TimeoutSeconds { get; init; } // connect_timeout
-
- // Additional parameters preserved across conversions
- public IReadOnlyDictionary Properties { get; init; } =
- new Dictionary();
-}
-
-///
-/// Codec for a specific connection string format (parse and format only for its own format).
-/// Do not implement format specifics yet; provide interface only.
-///
-public interface IConnectionStringCodec
-{
- ConnStringFormat Format { get; }
- string FormatName { get; }
-
- // Parse input in this codec's format into a descriptor.
- Result TryParse(string input);
-
- // Format a descriptor into this codec's format.
- Result TryFormat(ConnectionDescriptor descriptor);
-}
-
-///
-/// High-level service to detect, parse, format and convert between formats.
-/// Implementations will compose specific codecs.
-///
-public interface IConnectionStringService
-{
- Result DetectFormat(string input);
-
- Result ParseToDescriptor(string input);
-
- Result FormatFromDescriptor(ConnectionDescriptor descriptor, ConnStringFormat targetFormat);
-
- Result Convert(string input, ConnStringFormat targetFormat);
-}
diff --git a/pgLabII.PgUtils/ConnectionStrings/ConnStringFormat.cs b/pgLabII.PgUtils/ConnectionStrings/ConnStringFormat.cs
new file mode 100644
index 0000000..9e97943
--- /dev/null
+++ b/pgLabII.PgUtils/ConnectionStrings/ConnStringFormat.cs
@@ -0,0 +1,9 @@
+namespace pgLabII.PgUtils.ConnectionStrings;
+
+public enum ConnStringFormat
+{
+ Libpq,
+ Npgsql,
+ Url,
+ Jdbc
+}
\ No newline at end of file
diff --git a/pgLabII.PgUtils/ConnectionStrings/ConnectionDescriptor.cs b/pgLabII.PgUtils/ConnectionStrings/ConnectionDescriptor.cs
new file mode 100644
index 0000000..f8dfe8c
--- /dev/null
+++ b/pgLabII.PgUtils/ConnectionStrings/ConnectionDescriptor.cs
@@ -0,0 +1,29 @@
+using Npgsql;
+
+namespace pgLabII.PgUtils.ConnectionStrings;
+
+///
+/// Canonical, format-agnostic representation of a PostgreSQL connection.
+/// Keep minimal fields for broad interoperability; store extras in Properties.
+///
+public sealed class ConnectionDescriptor
+{
+ public string? Name { get; init; }
+
+ // Primary hosts (support multi-host). If empty, implies localhost default.
+ public IReadOnlyList Hosts { get; init; } = new List();
+
+ public string? Database { get; init; }
+ public string? Username { get; init; }
+ public string? Password { get; init; }
+
+ public SslMode? SslMode { get; init; }
+
+ // Common optional fields
+ public string? ApplicationName { get; init; }
+ public int? TimeoutSeconds { get; init; } // connect_timeout
+
+ // Additional parameters preserved across conversions
+ public IReadOnlyDictionary Properties { get; init; } =
+ new Dictionary();
+}
\ No newline at end of file
diff --git a/pgLabII.PgUtils/ConnectionStrings/HostEndpoint.cs b/pgLabII.PgUtils/ConnectionStrings/HostEndpoint.cs
new file mode 100644
index 0000000..b03d3f7
--- /dev/null
+++ b/pgLabII.PgUtils/ConnectionStrings/HostEndpoint.cs
@@ -0,0 +1,9 @@
+using System.Collections.Generic;
+
+namespace pgLabII.PgUtils.ConnectionStrings;
+
+public sealed class HostEndpoint
+{
+ public string Host { get; init; } = string.Empty;
+ public ushort? Port { get; init; }
+}
diff --git a/pgLabII.PgUtils/ConnectionStrings/IConnectionStringCodec.cs b/pgLabII.PgUtils/ConnectionStrings/IConnectionStringCodec.cs
new file mode 100644
index 0000000..050c46f
--- /dev/null
+++ b/pgLabII.PgUtils/ConnectionStrings/IConnectionStringCodec.cs
@@ -0,0 +1,19 @@
+using FluentResults;
+
+namespace pgLabII.PgUtils.ConnectionStrings;
+
+///
+/// Codec for a specific connection string format (parse and format only for its own format).
+/// Do not implement format specifics yet; provide interface only.
+///
+public interface IConnectionStringCodec
+{
+ ConnStringFormat Format { get; }
+ string FormatName { get; }
+
+ // Parse input in this codec's format into a descriptor.
+ Result TryParse(string input);
+
+ // Format a descriptor into this codec's format.
+ Result TryFormat(ConnectionDescriptor descriptor);
+}
\ No newline at end of file
diff --git a/pgLabII.PgUtils/ConnectionStrings/IConnectionStringService.cs b/pgLabII.PgUtils/ConnectionStrings/IConnectionStringService.cs
new file mode 100644
index 0000000..fb6f04d
--- /dev/null
+++ b/pgLabII.PgUtils/ConnectionStrings/IConnectionStringService.cs
@@ -0,0 +1,18 @@
+using FluentResults;
+
+namespace pgLabII.PgUtils.ConnectionStrings;
+
+///
+/// High-level service to detect, parse, format and convert between formats.
+/// Implementations will compose specific codecs.
+///
+public interface IConnectionStringService
+{
+ Result DetectFormat(string input);
+
+ Result ParseToDescriptor(string input);
+
+ Result FormatFromDescriptor(ConnectionDescriptor descriptor, ConnStringFormat targetFormat);
+
+ Result Convert(string input, ConnStringFormat targetFormat);
+}
\ No newline at end of file
diff --git a/pgLabII.PgUtils/ConnectionStrings/NpgsqlCodec.cs b/pgLabII.PgUtils/ConnectionStrings/NpgsqlCodec.cs
index 7e2f0fa..7aa6519 100644
--- a/pgLabII.PgUtils/ConnectionStrings/NpgsqlCodec.cs
+++ b/pgLabII.PgUtils/ConnectionStrings/NpgsqlCodec.cs
@@ -8,6 +8,21 @@ using Npgsql;
namespace pgLabII.PgUtils.ConnectionStrings;
+///
+/// Parser/formatter for Npgsql-style .NET connection strings. We intentionally do not
+/// rely on NpgsqlConnectionStringBuilder here because:
+/// - We need a lossless, format-agnostic round-trip to our ConnectionDescriptor, including
+/// unknown/extension keys and per-host port lists. NpgsqlConnectionStringBuilder normalizes
+/// names, may drop unknown keys or coerce values, which breaks lossless conversions.
+/// - We support multi-host with per-host ports and want to preserve the original textual
+/// representation across conversions. The builder flattens/rewrites these details.
+/// - We aim to keep pgLabII.PgUtils independent from Npgsql's evolving parsing rules and
+/// version-specific behaviors to ensure stable UX and deterministic tests.
+/// - We need symmetric formatting matching our other codecs (libpq/URL/JDBC) and consistent
+/// quoting rules across formats.
+/// If required, we still reference Npgsql for enums and interop types, but parsing/formatting
+/// is done by this small, well-tested custom codec for full control and stability.
+///
public sealed class NpgsqlCodec : IConnectionStringCodec
{
public ConnStringFormat Format => ConnStringFormat.Npgsql;
diff --git a/pgLabII.PgUtils/ConnectionStrings/PLAN.md b/pgLabII.PgUtils/ConnectionStrings/PLAN.md
new file mode 100644
index 0000000..b30d7fa
--- /dev/null
+++ b/pgLabII.PgUtils/ConnectionStrings/PLAN.md
@@ -0,0 +1,51 @@
+# Connection Strings Plan
+
+This document tracks the plan for supporting multiple PostgreSQL connection string formats, converting between them, and mapping to/from a canonical model.
+
+## Current Status (2025-08-30)
+
+Implemented:
+- Abstractions: `ConnStringFormat`, `HostEndpoint`, `ConnectionDescriptor`, `IConnectionStringCodec`, `IConnectionStringService`.
+- Codecs:
+ - `LibpqCodec` (libpq): parse/format; multi-host; `sslmode`, `application_name`, `connect_timeout`; quoting/escaping; preserves extras.
+ - `NpgsqlCodec` (.NET/Npgsql): parse/format; alias recognition; multi-host with single or per-host ports; `SSL Mode`, `Application Name`, `Timeout`; double-quote rules; preserves extras.
+- Tests for both codecs: parse, format, round-trip, edge quoting.
+
+Not yet implemented:
+- URL (postgresql://) codec ✓
+- JDBC (jdbc:postgresql://) codec
+- Composite `ConnectionStringService` (detect + convert) ✓
+- Mapping helpers to/from `ServerConfiguration` ✓
+
+## Updated Plan
+
+1. Define canonical model and interfaces for connection strings. ✓
+2. Establish normalization strategy for parameter aliases and extra `Properties` handling. ✓
+3. Implement format-specific codecs:
+ - libpq codec (parse/format; multi-host, quoting, sslmode, timeout, extras). ✓
+ - Npgsql codec (parse/format; aliases, multi-host/ports, quoting, ssl mode, timeout, extras). ✓
+ - URL (postgresql://) codec (parse/format; userinfo, host[:port], db, query params, percent-encoding). ✓
+ - JDBC (jdbc:postgresql://) codec (parse/format; hosts, ports, db, properties; URL-like semantics).
+4. Composite conversion service:
+ - Implement `ConnectionStringService` composing codecs, detecting formats, converting via `ConnectionDescriptor`, and resolving alias priorities.
+5. Mapping with application model:
+ - Add mapping utilities between `ConnectionDescriptor` and `ServerConfiguration` (primary host/port, db, SSL mode), with sensible defaults.
+6. Validation and UX:
+ - Validation for malformed inputs & edge cases (mismatched host/port counts, invalid SSL mode, missing db/host, IPv6 bracket handling).
+ - Ensure sensitive fields (password) are masked in logs/preview.
+7. Tests:
+ - Unit tests for URL and JDBC codecs; composite service detect/convert; mapping functions; cross-format round-trips; edge cases (spaces, quotes, unicode, IPv6, percent-encoding).
+8. Documentation:
+ - Keep this plan updated and enrich XML docs on codecs/service including alias mappings and quoting/escaping rules per format.
+
+## Next Small Step
+
+Implement the URL (postgresql://) codec with unit tests. Scope:
+- Parse: `postgresql://[user[:password]@]host1[:port1][,hostN[:portN]]/[database]?param=value&...`
+ - Support percent-decoding for user, password, database, and query values.
+ - Handle IPv6 literals in `[::1]` form; allow multiple hosts with optional per-host ports.
+ - Map common params: `sslmode`, `application_name`, `connect_timeout` and preserve other query params in `Properties`.
+- Format: Build a URL using percent-encoding where required; emit multi-hosts and parameters from `Properties` not already emitted.
+- Tests: basic parse/format, quoting/percent-encoding, multi-host with mixed ports, round-trips.
+
+After that, implement the composite `ConnectionStringService` to detect/convert across libpq, Npgsql, and URL formats.
diff --git a/pgLabII.Tests/Services/ServerConfigurationMappingTests.cs b/pgLabII.Tests/Services/ServerConfigurationMappingTests.cs
new file mode 100644
index 0000000..185f3cc
--- /dev/null
+++ b/pgLabII.Tests/Services/ServerConfigurationMappingTests.cs
@@ -0,0 +1,142 @@
+using System.Collections.Generic;
+using Npgsql;
+using pgLabII.Model;
+using pgLabII.PgUtils.ConnectionStrings;
+using pgLabII.Services;
+using Xunit;
+
+namespace pgLabII.Tests.Services;
+
+public class ServerConfigurationMappingTests
+{
+ [Fact]
+ public void ToDescriptor_Basic_MapsExpectedFields()
+ {
+ var cfg = new ServerConfiguration
+ {
+ Name = "Prod",
+ Host = "db.example.com",
+ Port = 5433,
+ InitialDatabase = "appdb",
+ DefaultSslMode = SslMode.Require,
+ User = new ServerUser { Name = "alice", Password = "secret" }
+ };
+
+ var extra = new Dictionary{{"search_path","public"}};
+ var d = ServerConfigurationMapping.ToDescriptor(cfg, applicationName: "pgLabII", timeoutSeconds: 15, extraProperties: extra);
+
+ Assert.Equal("Prod", d.Name);
+ Assert.Single(d.Hosts);
+ Assert.Equal("db.example.com", d.Hosts[0].Host);
+ Assert.Equal((ushort)5433, d.Hosts[0].Port);
+ Assert.Equal("appdb", d.Database);
+ Assert.Equal("alice", d.Username);
+ Assert.Equal("secret", d.Password);
+ Assert.Equal(SslMode.Require, d.SslMode);
+ Assert.Equal("pgLabII", d.ApplicationName);
+ Assert.Equal(15, d.TimeoutSeconds);
+ Assert.True(d.Properties.ContainsKey("search_path"));
+ Assert.Equal("public", d.Properties["search_path"]);
+ }
+
+ [Fact]
+ public void ToDescriptor_OmitsEmptyFields()
+ {
+ var cfg = new ServerConfiguration
+ {
+ Name = "Empty",
+ Host = "",
+ InitialDatabase = "",
+ User = new ServerUser { Name = "", Password = "" }
+ };
+
+ var d = ServerConfigurationMapping.ToDescriptor(cfg);
+
+ Assert.Empty(d.Hosts);
+ Assert.Null(d.Database);
+ Assert.Null(d.Username);
+ Assert.Null(d.Password);
+ }
+
+ [Fact]
+ public void FromDescriptor_CreatesNew_UsesFirstHost()
+ {
+ var desc = new ConnectionDescriptor
+ {
+ Name = "Staging",
+ Hosts = new []
+ {
+ new HostEndpoint{ Host = "host1", Port = 5432 },
+ new HostEndpoint{ Host = "host2", Port = 5434 }
+ },
+ Database = "stagedb",
+ Username = "bob",
+ Password = "pwd",
+ SslMode = SslMode.VerifyFull
+ };
+
+ var cfg = ServerConfigurationMapping.FromDescriptor(desc);
+
+ Assert.Equal("Staging", cfg.Name);
+ Assert.Equal("host1", cfg.Host);
+ Assert.Equal((ushort)5432, cfg.Port);
+ Assert.Equal("stagedb", cfg.InitialDatabase);
+ Assert.Equal(SslMode.VerifyFull, cfg.DefaultSslMode);
+ Assert.Equal("bob", cfg.User.Name);
+ Assert.Equal("pwd", cfg.User.Password);
+ }
+
+ [Fact]
+ public void FromDescriptor_UpdatesExisting_PreservesMissing()
+ {
+ var existing = new ServerConfiguration
+ {
+ Name = "Existing",
+ Host = "keep-host",
+ Port = 5432,
+ InitialDatabase = "keepdb",
+ DefaultSslMode = SslMode.Prefer,
+ User = new ServerUser { Name = "keepuser", Password = "keeppwd" }
+ };
+
+ // Descriptor missing db and user/pass and sslmode
+ var desc = new ConnectionDescriptor
+ {
+ Hosts = new [] { new HostEndpoint{ Host = "new-host" } }
+ };
+
+ var cfg = ServerConfigurationMapping.FromDescriptor(desc, existing);
+
+ Assert.Equal("new-host", cfg.Host);
+ Assert.Equal((ushort)5432, cfg.Port); // unchanged
+ Assert.Equal("keepdb", cfg.InitialDatabase); // preserved
+ Assert.Equal(SslMode.Prefer, cfg.DefaultSslMode); // preserved
+ Assert.Equal("keepuser", cfg.User.Name); // preserved
+ Assert.Equal("keeppwd", cfg.User.Password); // preserved
+ }
+
+ [Fact]
+ public void Roundtrip_Basic()
+ {
+ var cfg = new ServerConfiguration
+ {
+ Name = "Round",
+ Host = "localhost",
+ Port = 5432,
+ InitialDatabase = "postgres",
+ DefaultSslMode = SslMode.Allow,
+ User = new ServerUser { Name = "me", Password = "pw" }
+ };
+
+ var d = ServerConfigurationMapping.ToDescriptor(cfg);
+ var cfg2 = ServerConfigurationMapping.FromDescriptor(d);
+
+ Assert.Equal(cfg.Name, cfg2.Name);
+ Assert.Equal(cfg.Host, cfg2.Host);
+ Assert.Equal(cfg.Port, cfg2.Port);
+ Assert.Equal(cfg.InitialDatabase, cfg2.InitialDatabase);
+ Assert.Equal(cfg.DefaultSslMode, cfg2.DefaultSslMode);
+ Assert.Equal(cfg.User.Name, cfg2.User.Name);
+ Assert.Equal(cfg.User.Password, cfg2.User.Password);
+ }
+}
diff --git a/pgLabII.Tests/pgLabII.Tests.csproj b/pgLabII.Tests/pgLabII.Tests.csproj
new file mode 100644
index 0000000..f10cf0d
--- /dev/null
+++ b/pgLabII.Tests/pgLabII.Tests.csproj
@@ -0,0 +1,23 @@
+
+
+ net9.0
+ false
+
+
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
+
+
diff --git a/pgLabII.sln b/pgLabII.sln
index f519b85..06f47f7 100644
--- a/pgLabII.sln
+++ b/pgLabII.sln
@@ -18,6 +18,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "pgLabII.PgUtils", "pgLabII.
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "pgLabII.PgUtils.Tests", "pgLabII.PgUtils.Tests\pgLabII.PgUtils.Tests.csproj", "{915C5439-4CF5-4625-AB7B-24F0E34E5B5F}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "pgLabII.Tests", "pgLabII.Tests\pgLabII.Tests.csproj", "{3A83F6AA-A445-4A1B-BB32-230640DEDF24}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -58,6 +60,14 @@ Global
{915C5439-4CF5-4625-AB7B-24F0E34E5B5F}.Release|Any CPU.Build.0 = Release|Any CPU
{915C5439-4CF5-4625-AB7B-24F0E34E5B5F}.Release|x64.ActiveCfg = Release|x64
{915C5439-4CF5-4625-AB7B-24F0E34E5B5F}.Release|x64.Build.0 = Release|x64
+ {3A83F6AA-A445-4A1B-BB32-230640DEDF24}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {3A83F6AA-A445-4A1B-BB32-230640DEDF24}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {3A83F6AA-A445-4A1B-BB32-230640DEDF24}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {3A83F6AA-A445-4A1B-BB32-230640DEDF24}.Debug|x64.Build.0 = Debug|Any CPU
+ {3A83F6AA-A445-4A1B-BB32-230640DEDF24}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {3A83F6AA-A445-4A1B-BB32-230640DEDF24}.Release|Any CPU.Build.0 = Release|Any CPU
+ {3A83F6AA-A445-4A1B-BB32-230640DEDF24}.Release|x64.ActiveCfg = Release|Any CPU
+ {3A83F6AA-A445-4A1B-BB32-230640DEDF24}.Release|x64.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/pgLabII/Services/ServerConfigurationMapping.cs b/pgLabII/Services/ServerConfigurationMapping.cs
new file mode 100644
index 0000000..9f51efd
--- /dev/null
+++ b/pgLabII/Services/ServerConfigurationMapping.cs
@@ -0,0 +1,102 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Npgsql;
+using pgLabII.Model;
+using pgLabII.PgUtils.ConnectionStrings;
+
+namespace pgLabII.Services;
+
+///
+/// Helpers to map between the app's ServerConfiguration model and the canonical ConnectionDescriptor
+/// used by connection string codecs. This keeps PgUtils decoupled from the app model.
+///
+public static class ServerConfigurationMapping
+{
+ ///
+ /// Creates a ConnectionDescriptor from a ServerConfiguration.
+ /// Notes:
+ /// - Only maps fields that exist on ServerConfiguration: Name, Host/Port (single host), InitialDatabase,
+ /// DefaultSslMode, User.Name/Password.
+ /// - ApplicationName and TimeoutSeconds don't exist on ServerConfiguration; we preserve any passed-in
+ /// values via optional parameters or Properties if provided by caller.
+ ///
+ public static ConnectionDescriptor ToDescriptor(ServerConfiguration cfg,
+ string? applicationName = null,
+ int? timeoutSeconds = null,
+ IReadOnlyDictionary? extraProperties = null)
+ {
+ if (cfg == null) throw new ArgumentNullException(nameof(cfg));
+
+ var hosts = new List();
+ if (!string.IsNullOrWhiteSpace(cfg.Host))
+ {
+ hosts.Add(new HostEndpoint { Host = cfg.Host.Trim(), Port = cfg.Port });
+ }
+
+ var props = new Dictionary(StringComparer.OrdinalIgnoreCase);
+ if (extraProperties != null)
+ {
+ foreach (var kv in extraProperties)
+ props[kv.Key] = kv.Value;
+ }
+
+ return new ConnectionDescriptor
+ {
+ Name = cfg.Name,
+ Hosts = hosts,
+ Database = string.IsNullOrWhiteSpace(cfg.InitialDatabase) ? null : cfg.InitialDatabase,
+ Username = string.IsNullOrWhiteSpace(cfg.User?.Name) ? null : cfg.User!.Name,
+ Password = string.IsNullOrEmpty(cfg.User?.Password) ? null : cfg.User!.Password,
+ SslMode = cfg.DefaultSslMode,
+ ApplicationName = applicationName,
+ TimeoutSeconds = timeoutSeconds,
+ Properties = props
+ };
+ }
+
+ ///
+ /// Updates or creates a ServerConfiguration from a ConnectionDescriptor.
+ /// - If existing is supplied, updates mutable fields and preserves UI-related fields (color, commands, etc.).
+ /// - If descriptor has multiple hosts, the first is mapped to Host/Port.
+ /// - If descriptor omits sslmode/database/username/password, existing values are preserved (if any).
+ ///
+ public static ServerConfiguration FromDescriptor(ConnectionDescriptor descriptor, ServerConfiguration? existing = null)
+ {
+ if (descriptor == null) throw new ArgumentNullException(nameof(descriptor));
+ var cfg = existing ?? new ServerConfiguration();
+
+ // Name
+ if (!string.IsNullOrWhiteSpace(descriptor.Name))
+ cfg.Name = descriptor.Name!;
+
+ // Host/Port: take first
+ if (descriptor.Hosts != null && descriptor.Hosts.Count > 0)
+ {
+ var h = descriptor.Hosts[0];
+ if (!string.IsNullOrWhiteSpace(h.Host))
+ cfg.Host = h.Host;
+ if (h.Port.HasValue)
+ cfg.Port = h.Port.Value;
+ }
+
+ // Database
+ if (!string.IsNullOrWhiteSpace(descriptor.Database))
+ cfg.InitialDatabase = descriptor.Database!;
+
+ // SSL Mode
+ if (descriptor.SslMode.HasValue)
+ cfg.DefaultSslMode = descriptor.SslMode.Value;
+
+ // User
+ if (cfg.User == null)
+ cfg.User = new ServerUser();
+ if (!string.IsNullOrWhiteSpace(descriptor.Username))
+ cfg.User.Name = descriptor.Username!;
+ if (!string.IsNullOrEmpty(descriptor.Password))
+ cfg.User.Password = descriptor.Password!;
+
+ // Nothing to do for ApplicationName/TimeoutSeconds here; not represented in ServerConfiguration.
+ return cfg;
+ }
+}
diff --git a/pgLabII/pgLabII.csproj b/pgLabII/pgLabII.csproj
index f2c36f9..b0f4298 100644
--- a/pgLabII/pgLabII.csproj
+++ b/pgLabII/pgLabII.csproj
@@ -41,4 +41,8 @@
runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+