---
title: Configuration settings
summary: Configuration keys consumed by this library, retrieval methods, and runtime dependencies.
status: draft
owner: Framework
last_reviewed: 2026-10-02
library: SAIF.NetCore.NHibernate
reference_type: package
---

# Configuration settings

This library reads no configuration keys of its own — a search of every source file found no `IConfiguration`, `GetSettingValue`, `GetSecureSetting`, `HasSetting`, `ConfigurationManager`, or `Environment.GetEnvironmentVariable` usage. Instead, it exposes a consumer-facing configuration surface: the named-options POCO `UnitOfWorkOptions` (with non-trivial defaults), the abstract configurer `ConfigureUnitOfWorkOptions` that each consuming application subclasses once per logical database, and two registration parameters on `AddNhibernateServices`.

## Configuration retrieval methods

| Method | Class / Interface | Namespace | Usage pattern |
| --- | --- | --- | --- |
| Named options type | `UnitOfWorkOptions` | `SAIF.NetCore.NHibernate.Options` | POCO with seven settable properties; its parameterless constructor applies the library defaults. One named instance describes one logical database connection. |
| Abstract named-options configurer | `ConfigureUnitOfWorkOptions : IConfigureNamedOptions<UnitOfWorkOptions>` | `SAIF.NetCore.NHibernate.Options` | The consumer subclasses it and registers it with `services.ConfigureOptions<TSubclass>()`. The subclass **must implement** `UniqueName`, `Database`, and `GetConnectionString()`. `GetConnectionString()` is called from the **base class's constructor** (`this._connectionString = GetConnectionString();`) — a virtual/abstract call executing before the derived class's own constructor body runs. If the override depends on fields the derived constructor itself assigns, those fields are not yet initialized when `GetConnectionString()` runs, so it can read nulls/defaults instead of intended values; do not have `GetConnectionString()` depend on derived-constructor-assigned state. |
| Engine selector | `DatabaseType` enum | `SAIF.NetCore.NHibernate.Options` | `Oracle = 0`, `Sql = 1`, `PostGreSql = 2`. |
| Registration parameters | `IServicesCollectionExtensions.AddNhibernateServices(IEnumerable<Assembly>, bool)` / `AddNhibernateServices<T>(bool)` | `SAIF.NetCore.NHibernate.Extensions` | Code-level settings, fixed at registration time and passed to the singleton `UnitOfWorkFactory` constructor. |
| Options resolution | `IOptionsSnapshot<UnitOfWorkOptions>.Get(name)` | `Microsoft.Extensions.Options` | `UnitOfWorkAccessor.UnitOfWork(key)` resolves the named options on every call; it is scoped. |

The library itself makes **no** `IEnvironmentClient` member calls, no `IConfiguration` reads, and no environment-variable reads. It only injects `IEnvironmentClient`/`IDBConnectionClient` so that consumer subclasses can call them inside their own `GetConnectionString()` override.

## Library-exposed settings — UnitOfWorkOptions named options

One named instance exists per `ConfigureUnitOfWorkOptions` subclass; the name is the subclass's `UniqueName`.

| # | Property | Type | Library default | Who sets it | NHibernate / runtime effect | When it takes effect |
| --- | --- | --- | --- | --- | --- | --- |
| UO1 | `Name` | `string` | `"default"` | Library, in `ConfigureUnitOfWorkOptions.Configure` (= `UniqueName`) | Key of the per-factory NHibernate configuration cache; copied to `IUnitOfWork.Name`. | Every resolution |
| UO2 | `Database` | `DatabaseType` | `Oracle` | Consumer subclass's abstract `Database` property | Selects engine configuration (`OracleConfiguration`/`SqlConfiguration`/`PostGreSqlConfiguration`) and the `SystemDate` session helper. An unrecognized value falls back to Oracle ([BR-NH-07](business-rules.md#br-nh-07-unrecognized-database-type-falls-back-to-oracle)) | Engine configuration: first build per name (cached). Session helper: every unit of work. |
| UO3 | `ConnectionString` | `string` | `null` (no default) | Consumer subclass's abstract `GetConnectionString()`, called once from the base constructor and cached | NHibernate `connection.connection_string`; key of the static, process-wide session-factory cache. Required — `GetUnitOfWork` throws `InvalidOperationException` when null/empty ([BR-NH-04](business-rules.md#br-nh-04-non-empty-connection-string-required)). Contains credentials. | First build per connection string (cached for process lifetime) |
| UO4 | `BatchSize` | `int` | `20` | Consumer override only (not set by the library's configurer) | NHibernate `adonet.batch_size` | First build per session-factory key only |
| UO5 | `ShowSql` | `bool` | `true` | Consumer override only | NHibernate `show_sql` — echoes every generated SQL statement to stdout | First build per session-factory key only |
| UO6 | `DriverFormatSql` | `bool` | `true` | Consumer override only | FluentNHibernate `.FormatSql()` on the driver configurer (pretty-printed, multi-line SQL output) | First build per session-factory key only |
| UO7 | `StatelessSessionStrategy` | `bool` | `false` (stateful `ISession`) | Consumer override only | `true` selects `IStatelessSession`/`StatelessSessionStrategy`; `false` selects `ISession`/`StatefulSessionStrategy` ([BR-NH-10](business-rules.md#br-nh-10-session-type-selected-by-statelesssessionstrategy-option)) | Every unit of work (read per `GetUnitOfWork` call, not cached) |

> **Caching caveat:** UO4–UO6 are captured only when the session factory is first built for a given connection string, and that factory is cached process-wide. Changing these options after the first unit of work has no effect until the process restarts, and two named instances sharing a connection string also share the first one's `BatchSize`/`ShowSql`/`DriverFormatSql`.* UO7 and the session-helper part of UO2 are evaluated on every call. **Nested-cache mismatch:** `UO3`'s session-factory cache (keyed by `ConnectionString`) and the underlying engine-`Configuration` cache (keyed by `Name`, see [Build session factory](operations/build-session-factory.md)) are two separate caches with different keys. If two calls share the same `Name` but supply different `ConnectionString` values, a new session factory IS built (connection-string cache miss), but the reused, name-keyed `Configuration` object's `ConnectionString` field still holds the FIRST connection string ever seen for that `Name` — so the newly built factory silently connects using the OLD connection string rather than the one just supplied. A new connection-string key does not, by itself, guarantee that the new connection string is actually used end-to-end.

### Registration parameters

| Parameter | Method | Type | Default | Effect |
| --- | --- | --- | --- | --- |
| `assemblyMappings` | `AddNhibernateServices(IEnumerable<Assembly>, bool)` | `IEnumerable<Assembly>` | none (required) | Assemblies scanned for both Fluent `ClassMap<T>` and embedded HBM mappings; fixed for the lifetime of the singleton factory. |
| `T` | `AddNhibernateServices<T>(bool)` | type parameter | — | `Assembly.GetAssembly(typeof(T))` — resolves to a single mapping assembly. |
| `debugOutputSql` | Both overloads | `bool` | `false` on the generic overload; required on the `IEnumerable<Assembly>` overload | When `true`, stateful sessions open with `SqlDebugOutputInterceptor`, which traces SQL to `Trace` only while a debugger is attached. Stateless sessions never receive it. Independent of `ShowSql`. |

### Override mechanisms for UO4–UO7

Unless a consumer applies one of the mechanisms below, the library defaults apply — meaning SQL logging and SQL formatting are **on**.

| Mechanism | Example | Scope |
| --- | --- | --- |
| Override `Configure` in the subclass | `public override void Configure(string n, UnitOfWorkOptions o) { base.Configure(n, o); if (n == UniqueName) { o.ShowSql = false; } }` | That named database only |
| Register an additional named configure delegate | `services.Configure<UnitOfWorkOptions>("myname", o => o.ShowSql = false);` | That named database only |
| Apply to every named instance | `services.ConfigureAll<UnitOfWorkOptions>(o => o.ShowSql = false);` | All databases — can also overwrite `Name`/`Database`/`ConnectionString` if misused* |
| Bind from `IConfiguration` (consumer choice) | `services.Configure<UnitOfWorkOptions>("myname", Configuration.GetSection("NHibernate:myname"))` | The library supplies no section name or binding; any `appsettings.json` keys here are entirely consumer-defined |
| Construct `UnitOfWorkOptions` directly | `new UnitOfWorkOptions { Name = "test", Database = DatabaseType.Oracle, ConnectionString = "<provider-specific connection string>" }` passed straight to `UnitOfWorkFactory.GetUnitOfWork` | Bypasses the options pipeline entirely |

## Environment API settings

The library reads no Environment API keys itself. Every `IEnvironmentClient` key used with this library is consumer-defined inside the subclass's `GetConnectionString()` override. The rows below are illustrative examples from the test project, not library requirements.

| Setting key (example) | Method | Secure | Purpose | Status |
| --- | --- | --- | --- | --- |
| `oracledatabase` | `IEnvironmentClient.GetSettingValue(string)` | No | Oracle instance name, passed to `IDBConnectionClient.GetOracleConnection(instance, userName)` | Example (test-only, not shipped) |
| `netframeworksqlserver4` | `GetSettingValue(string)` | No | SQL Server host, passed to `GetSqlServerConnection(server, instance, userName)` | Example (test-only) |

## Database connection configuration

The library is a connection-consumer framework: it defines no connection names or keys of its own. Each consumer defines one connection per `ConfigureUnitOfWorkOptions` subclass.

| Element | Value |
| --- | --- |
| Connection identity | The subclass's `UniqueName`, which becomes the `UnitOfWorkOptions` name |
| Connection-string source | The consumer's `GetConnectionString()` override — the standard pattern calls `IDBConnectionClient.GetOracleConnection`/`GetSqlServerConnection`/`GetPostgreSqlServerConnection` |
| Providers | Oracle: `Oracle.ManagedDataAccess.Core` via `OracleManagedDataClientDriver` (no native client install needed). SQL Server: NHibernate's default `MsSql2008` driver. PostgreSQL: `Npgsql` via `NpgsqlDriver`* |
| Resolution timing | Once per configurer construction (base constructor) — in practice once per `IOptionsSnapshot` scope, i.e. once per request* |
| Caching | Session factory cached process-wide, keyed by the connection string itself; **but** the nested, name-keyed `Configuration` cache can reuse a stale connection string if `Name` collides with a different `ConnectionString` — see the caching caveat above and [Build session factory](operations/build-session-factory.md). |

## Secure settings

The library reads no secure settings itself. Connection strings (UO3) are secrets but are not configuration keys the library retrieves directly — they are produced by the consumer's `GetConnectionString()` and then held in `UnitOfWorkOptions.ConnectionString`, in each configurer's private cached field, and as the key of the static `SessionFactories` dictionary for the life of the process.

## Transitive configuration (from dependencies)

Settings a consuming application must supply when it follows the standard `ConfigureUnitOfWorkOptions` pattern; the library itself has no `ProjectReference`s.

| Setting / service | Kind | Required | Source dependency | Notes |
| --- | --- | --- | --- | --- |
| `IEnvironmentClient` registration | Required service (not registered by this library) | Yes — the configurer constructor requires it | `SAIF.NetCore.ServiceContracts` (contract); `SAIF.NetCore.EnvironmentClient` (implementation) | Usually registered by `SAIF.NetCore.ServiceExtensions` (`CoreAPIStartup`). The full bootstrap-key catalog (which keys are mandatory vs. optional, and how the process resolves them) is documented on the owning library's own page: [SAIF.NetCore.EnvironmentClient configuration](../saif-netcore-environmentclient/configuration.md). In summary for this library's own usage: the OS process environment variable `EnvironmentApiUrl` is **mandatory** for every host (read once, before the client's constructor body, independent of `IConfiguration`) — without it, `IEnvironmentClient` construction fails with `MissingEnvironmentUrlVariableException` and the consumer's `ConfigureUnitOfWorkOptions` subclass constructor cannot complete. `AppSettings:ApplicationHost`/`AppSettings:EnvApiDeveloperOverrideAllowed`/`AppSettings:EnvApiDeveloperSettingsOverride` are **developer-machine-only** optional overrides with no effect on non-developer hosts. When the registered implementation is `SecureEnvironmentClient`, its constructor requires `COREAPIENABLED == "1"` **or** a developer-machine host (`IsDeveloperMachine()`) merely to construct — this gate is evaluated unconditionally at construction time, regardless of whether any later call through this client resolves a secure or non-secure setting; a non-developer host that fails the gate cannot construct the client at all, so this library's `ConfigureUnitOfWorkOptions` subclass constructor cannot complete either. Registering a different, non-secure `IEnvironmentClient` implementation avoids this gate entirely — that is a different implementation choice, not a case of the secure client "only mattering" when secure settings are read. |
| `IDBConnectionClient` registration | Required service | Yes (constructor) | `SAIF.NetCore.ServiceContracts` (contract); `SAIF.NetCore.DBConnectionClient` (implementation) | Usually registered by `SAIF.NetCore.ServiceExtensions`. The full endpoint-key catalog is documented on the owning library's own page: [SAIF.NetCore.DBConnectionClient configuration](../saif-netcore-dbconnectionclient/configuration.md). In summary for this library's own usage: `ConnectionClient.Setup()` reads `DBConnectionClient:ServiceUrl`, `DBConnectionClient:SqlConnectionEndpoint`, `DBConnectionClient:PostgreSqlConnectionEndpoint`, and `DBConnectionClient:OracleConnectionEndpoint` **unconditionally and sequentially, inside the constructor**, regardless of which `DatabaseType` the consumer later uses — there is no engine branch at construction time. This is distinct from later, engine-specific usage: only the matching `GetOracleConnection`/`GetSqlServerConnection`/`GetPostgreSqlServerConnection` call that this library's subclasses actually invoke at runtime uses the corresponding endpoint value. |
| `IOptionsSnapshot<UnitOfWorkOptions>` / options infrastructure | Required service | Yes | Host (ASP.NET Core / Generic Host registers `AddOptions` by default) | Used by `UnitOfWorkAccessor` |
| `DBConnectionClient:ServiceUrl`, `DBConnectionClient:OracleConnectionEndpoint`, `DBConnectionClient:SqlConnectionEndpoint`, `DBConnectionClient:PostgreSqlConnectionEndpoint` | Environment API | Required unconditionally at construction | `SAIF.NetCore.DBConnectionClient` | All four keys are read by the `ConnectionClient` constructor for every host, independent of which `DatabaseType` engine is actually used afterward |
| `dbconnectionclient:connectionservicecreds` | Secure Environment API setting | Required when `ServiceExtensions.RegisterDBConnectionClient` registers the client | `SAIF.NetCore.ServiceExtensions` | — |

## Configuration dependencies

| Dependency | Type | Required at | Impact if missing |
| --- | --- | --- | --- |
| A `ConfigureUnitOfWorkOptions` subclass registered for a given name | Code/DI | Startup, resolved at first use | A `UnitOfWorkAccessor.UnitOfWork(key)` call for an unregistered name yields default options with no connection string, which throws `InvalidOperationException` on `GetUnitOfWork` |
| `IEnvironmentClient` / `IDBConnectionClient` registration | DI service | Startup | The consumer subclass's constructor fails to resolve, which fails DI container validation/first resolution |
| Valid connection-string resolution in `GetConnectionString()` | Runtime | First options resolution per name | An exception there surfaces when the options are first resolved, before any session work begins |

## Related documentation
- [Index](index.md)
- [Overview](overview.md)
- [Architecture](architecture.md)
- [Dependencies](dependencies.md)
- [Operations and processes](operations-processes.md)
- [Business rules](business-rules.md)
- [Database tables](database-tables.md)
- [SQL catalog](sql/index.md)
- [Service registration](service-registration.md)
- [API reference](api/index.md)

> **Note:** Statements marked with `*` were inferred from code structure, naming
> conventions, or contextual evidence where direct confirmation was not available
> in source artifacts. Statements marked with `**` could not be confirmed; treat
> as unknown until verified.

## Sources
- `src/SAIF.NetCore.Nhibernate/SAIF.NetCore.NHibernate/SAIF.NetCore.NHibernate/Options/UnitOfWorkOptions.cs`
- `src/SAIF.NetCore.Nhibernate/SAIF.NetCore.NHibernate/SAIF.NetCore.NHibernate/Options/ConfigureUnitOfWorkOptions.cs`
- `src/SAIF.NetCore.Nhibernate/SAIF.NetCore.NHibernate/SAIF.NetCore.NHibernate/Options/DatabaseType.cs`
- `src/SAIF.NetCore.Nhibernate/SAIF.NetCore.NHibernate/SAIF.NetCore.NHibernate/Extensions/IServicesCollectionExtensions.cs`
- `src/SAIF.NetCore.Nhibernate/SAIF.NetCore.NHibernate/SAIF.NetCore.NHibernate/Factories/UnitOfWorkFactory.cs`
- `src/SAIF.NetCore.Nhibernate/SAIF.NetCore.NHibernate/SAIF.NetCore.NHibernate/Factories/Configuration/BaseConfiguration.cs`
- `src/SAIF.NetCore.Nhibernate/SAIF.NetCore.NHibernate/SAIF.NetCore.NHibernate/UnitOfWorkAccessor.cs`
- `src/SAIF.NetCore.Nhibernate/SAIF.NetCore.NHibernate/SAIF.NetCore.NHibernate.Tests/Options/ConfigureOracleUnitOfWork.cs` (test-fixture example of a `ConfigureUnitOfWorkOptions` subclass)
- `src/SAIF.NetCore.Nhibernate/SAIF.NetCore.NHibernate/SAIF.NetCore.NHibernate.Tests/Options/ConfigureSqlUnitOfWork.cs` (test-fixture example of a `ConfigureUnitOfWorkOptions` subclass)
