---
title: Register DB connection client
summary: Lazily constructs the DB connection client and bootstraps its credentials from a secure, hash-gated setting.
status: draft
owner: Framework.NCore maintainers
last_reviewed: 2026-10-02
library: SAIF.NetCore.ServiceExtensions
reference_type: package
operation_id: OP-03
trigger: First resolution of IDBConnectionClient after RegisterDBConnectionClient() has been called.
---

# Register DB connection client

This operation registers a lazily-constructed `IDBConnectionClient` and, on first resolution, bootstraps its credentials from a secure environment setting hashed against the `DBConnectionClient` assembly (not the consuming application's assembly).

| Field | Value |
| --- | --- |
| **Operation ID** | OP-03 |
| **Trigger** | Application startup code calls `RegisterDBConnectionClient()`; execution of steps 2–5 happens lazily on first `IDBConnectionClient` resolution. |
| **Inputs** | None (parameterless); relies on a previously registered `IHttpJsonClient` and `IEnvironmentClient`. |
| **Outputs** | A configured `IDBConnectionClient` singleton with DB-service credentials applied. |
| **Primary components** | `IServiceCollectionExtensions.RegisterDBConnectionClient` |
| **Entry point** | `src/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/IServiceCollectionExtensions.cs` (L62–75) |

## Steps

1. Register a Singleton `IDBConnectionClient` factory ([BR-SE-04](../business-rules.md#br-se-04-db-connection-credentials-are-fetched-as-a-secure%2C-hash-gated-setting))
   - Executed lazily: the factory body runs only on first resolve.
2. `new ConnectionClient(GetRequiredService<IHttpJsonClient>(), GetRequiredService<IEnvironmentClient>())`, whose constructor immediately calls its own `Setup()`: reads the four `DBConnectionClient` settings (`ServiceUrl`, `SqlConnectionEndpoint`, `PostgreSqlConnectionEndpoint`, `OracleConnectionEndpoint`) from the `DBConnectionClient` configuration section via `IEnvironmentClient.GetSettingValue`, calls `HttpClient.SetClientInfo(ServiceUrl, "DBConnectionClient", EnvClient.ApplicationHost, EnvClient.ApplicationName)` to set the client's identity/base URL, then calls `HttpClient.SetNtlmAuthentication()` with no credentials — this is the default/current process-identity NTLM context (the `HttpJsonClient` implementation's zero-argument overload), not an anonymous/unauthenticated mode.

   > **Origin**: `ConnectionClient`'s construction and connection-string resolution logic is implemented in [`Construct and configure client`](../../saif-netcore-dbconnectionclient/operations/construct-and-configure-client.md) (`SAIF.NetCore.DBConnectionClient`); the constructor/`Setup()` work summarized above runs synchronously as part of this factory, on first resolution of `IDBConnectionClient`.
3. `new SystemHashService(typeof(ConnectionClient).Assembly).Get(SystemType.WebApp)` — `Get` resolves the DBConnectionClient assembly (passed into the constructor, so `GetAssembly()` returns it directly rather than falling back to `Assembly.GetCallingAssembly()`) and calls `ComputeHash(SystemType.WebApp, "SAIF.NetCore.DBConnectionClient")`, the salted path (via `SaltShaker(DateTime.Now).SaltHash`), **not** the unsalted `ComputeHashNow`; the hash is keyed on the **DBConnectionClient assembly**, not the consuming application's assembly ([BR-SE-04](../business-rules.md#br-se-04-db-connection-credentials-are-fetched-as-a-secure%2C-hash-gated-setting))
   - `ComputeHash` runs two sub-phases: (1) build the identity string `"{systemName}-{(int)SystemType.WebApp}"`, encode it as UTF-16LE bytes, compute its SHA-256 digest, and Base64-encode the digest (44 characters) — the deterministic identity hash; (2) construct a `SaltShaker(DateTime.Now)` that hex-encodes the current minute/year/day/millisecond/second/hour/month, then `SaltHash` inserts each encoded date part at a pseudo-random position in the identity hash and prepends a 42-character location map recording where each part landed — so the returned credential embeds a recoverable timestamp without being a stable, cacheable value.

   > **Origin**: The full step-by-step identity-hash and salting detail summarized above is implemented in [`Compute salted system hash`](../../saif-netcore-systemhash/operations/compute-salted-system-hash.md) (`SAIF.NetCore.SystemHash`); this step supplies the DBConnectionClient assembly as the hash identity and `SystemType.WebApp` from `SAIF.NetCore.ServiceContracts`.
4. `env.GetSecureSetting("connectionservicecreds", "dbconnectionclient", hash)` ([BR-SE-04](../business-rules.md#br-se-04-db-connection-credentials-are-fetched-as-a-secure%2C-hash-gated-setting)), [BR-SE-01](../business-rules.md#br-se-01-non-secure-environment-clients-cannot-retrieve-secure-settings)
   - On a secure `IEnvironmentClient` (`SecureEnvironmentClient`): calls `CheckForUpdates()` directly first (not through `LoadSection`/`LoadSectionAsync`). This check has three possible outcomes, not a single "refresh if empty or elapsed" rule: an empty settings cache reloads the entire cache immediately with no network call; a non-empty cache before the 60-second throttle window issues no call and does not reload; a non-empty cache at or past that window issues a `GET /settings/lastRefreshed` probe and reloads only if that call succeeds and the server's timestamp is newer — see [Refresh check and cache reload](../../saif-netcore-environmentclient/operations/refresh-check-and-cache-reload.md) (`SAIF.NetCore.EnvironmentClient`) for the full branch detail. It then checks a local `SecureSettingCache` keyed on `"{section}.{configKey}"`; on a cache miss, it builds the secure-setting URL and (if `HasSetting` reports the key exists) issues a synchronous `HttpClient.Get(urlPath)` against the remote Environment API, then parses the response into the requested type. If the retrieved/cached value is `null`/default, it throws `EnvironmentClientException("No crypto data found for requested secure setting: " + configKey)`.
   - On failure: a non-secure `IEnvironmentClient` registration (see [Register environment client](register-environment-client.md)) throws `EnvironmentClientException` immediately, without attempting a remote call.

   > **Origin**: The cache/remote-call/error logic summarized above is implemented in [`Get secure setting`](../../saif-netcore-environmentclient/operations/get-secure-setting.md) (`SAIF.NetCore.EnvironmentClient`); this operation's factory invokes it directly (not merely registering it) the first time `IDBConnectionClient` is resolved.
5. `creds.Split("|")` → `SetupClientCredentials(credentials[0], credentials[1])`, which calls `HttpClient.SetNtlmAuthentication(username, password)` to replace the default/current-process-identity NTLM configured during `Setup()` with the retrieved service-account credentials ([BR-SE-05](../business-rules.md#br-se-05-the-credential-value-must-be-a-user%7Cpassword-pair))
   - On failure: no length check is performed, so a malformed value with fewer than two `|`-separated parts throws `IndexOutOfRangeException`.

   > **Origin**: Credential application is implemented in [`Apply service account credentials`](../../saif-netcore-dbconnectionclient/operations/apply-service-account-credentials.md) (`SAIF.NetCore.DBConnectionClient`); the `SetNtlmAuthentication(username, password)` call summarized above runs synchronously as part of this factory, on first resolution of `IDBConnectionClient`.

## Process flow

This diagram shows the full lazy-construction sequence that runs once, on first `IDBConnectionClient` resolution — it is not re-run on subsequent resolutions of the singleton.

```mermaid
flowchart TD
    Start([First IDBConnectionClient resolve]) --> S1[Construct ConnectionClient]
    S1 --> S2[Compute hash on DBConnectionClient assembly]
    S2 --> S3{Environment client is secure?}
    S3 -->|No| EndErr1([EnvironmentClientException])
    S3 -->|Yes| S4[GetSecureSetting connectionservicecreds]
    S4 --> S5{Value has 2+ parts split on pipe?}
    S5 -->|No| EndErr2([IndexOutOfRangeException])
    S5 -->|Yes| S6[SetupClientCredentials]
    S6 --> EndOk([DB connection client ready])
```

## Failure and edge-case behavior
- Non-secure environment client registration → `EnvironmentClientException` on credential fetch.
- Malformed credential value (no `|` or only one part) → `IndexOutOfRangeException`.

## Related documentation
- [Operations and processes](../operations-processes.md)
- [Business rules](../business-rules.md)

## Sources
- `src/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/IServiceCollectionExtensions.cs`
