Skip to content

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)
  2. Executed lazily: the factory body runs only on first resolve.
  3. 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); 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) - 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); 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), BR-SE-01 - 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) 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) 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); 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) - 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); 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.

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.

Sources

  • src/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/IServiceCollectionExtensions.cs