---
title: SAIF.NetCore.NHibernate business rules
summary: Enumerated business rules with per-rule trigger, enforcement, and outcomes.
status: draft
owner: Framework
last_reviewed: 2026-10-02
library: SAIF.NetCore.NHibernate
reference_type: package
---

# SAIF.NetCore.NHibernate business rules

This page captures the technical/framework rules enforced by this library. The library holds no domain or business logic of its own; these rules govern its data-access infrastructure (unit-of-work lifecycle, session caching, engine selection, and options defaults).

## BR-NH-01 Mapping assemblies fixed at registration

| Field | Value |
| --- | --- |
| **Statement** | A factory sees only the assemblies passed to `AddNhibernateServices` at registration time; mapping assemblies cannot be changed afterward. |
| **Trigger** | Host calls `AddNhibernateServices(assemblies, debugOutputSql)` or `AddNhibernateServices<T>(debugOutputSql)`. |
| **Enforcement** | `IServicesCollectionExtensions.AddUnitOfWorkFactory` captures the assemblies in the `UnitOfWorkFactory` constructor closure. |
| **Outcome** | The singleton `UnitOfWorkFactory` scans the same fixed assembly set for every session factory it builds for the life of the process. |
| **SQL** | [SQL-004](sql/SQL-004-test-connection.md) |
| **Source** | `src/SAIF.NetCore.Nhibernate/SAIF.NetCore.NHibernate/SAIF.NetCore.NHibernate/Extensions/IServicesCollectionExtensions.cs` |

---

## BR-NH-02 Options applied only to matching named instance

| Field | Value |
| --- | --- |
| **Statement** | A `ConfigureUnitOfWorkOptions` subclass writes `Name`, `Database`, and `ConnectionString` only when the resolved options name equals its own `UniqueName`. |
| **Trigger** | `IOptionsSnapshot<UnitOfWorkOptions>.Get(name)` on a cache miss for that `name` runs every registered `IConfigureNamedOptions<UnitOfWorkOptions>.Configure(name, options)`; a cache hit for an already-resolved name invokes no `Configure` call. |
| **Enforcement** | `ConfigureUnitOfWorkOptions.Configure(string, UnitOfWorkOptions)` guards the assignment with a name-equality check. |
| **Outcome** | Every registered configurer whose `UniqueName` matches the requested name applies its values; nothing in source prevents two different registered `ConfigureUnitOfWorkOptions` subclasses from declaring the same `UniqueName`, and if that happens, registration order determines which values are ultimately kept (last-registered-wins for overlapping properties, since .NET's named-options pipeline runs every matching `Configure` call in registration order). |
| **Source** | `src/SAIF.NetCore.Nhibernate/SAIF.NetCore.NHibernate/SAIF.NetCore.NHibernate/Options/ConfigureUnitOfWorkOptions.cs` |

---

## BR-NH-03 Connection string resolved once at construction

| Field | Value |
| --- | --- |
| **Statement** | The connection string is resolved once, when the configurer is constructed, not on every `Configure` call. |
| **Trigger** | DI constructs the `ConfigureUnitOfWorkOptions` subclass (typically once per `IOptionsSnapshot` scope). |
| **Enforcement** | The base constructor calls the abstract `GetConnectionString()` and caches the result in a private field. |
| **Outcome** | Repeated `Configure` calls reuse the cached string rather than re-resolving it; a `GetConnectionString()` exception surfaces at construction time, before any options are read. |
| **Source** | `src/SAIF.NetCore.Nhibernate/SAIF.NetCore.NHibernate/SAIF.NetCore.NHibernate/Options/ConfigureUnitOfWorkOptions.cs` |

---

## BR-NH-04 Non-empty connection string required

| Field | Value |
| --- | --- |
| **Statement** | A unit of work cannot be created without a non-empty connection string; the factory throws `InvalidOperationException` otherwise. |
| **Trigger** | `UnitOfWorkFactory.GetUnitOfWork(options)` is called with `options.ConnectionString` null or empty. |
| **Enforcement** | A guard clause at the top of `GetUnitOfWork` checks `string.IsNullOrEmpty(options.ConnectionString)` and throws before any session factory work begins. |
| **Outcome** | Callers that pass an unresolved or unknown named options key fail fast with a clear exception rather than building a broken session. |
| **SQL** | [SQL-004](sql/SQL-004-test-connection.md) |
| **Source** | `src/SAIF.NetCore.Nhibernate/SAIF.NetCore.NHibernate/SAIF.NetCore.NHibernate/Factories/UnitOfWorkFactory.cs` |

---

## BR-NH-05 Session factories cached by connection string

| Field | Value |
| --- | --- |
| **Statement** | NHibernate `ISessionFactory` instances are cached process-wide in a `static` dictionary keyed only by connection string. |
| **Trigger** | `UnitOfWorkFactory.GetUnitOfWork` is called for any options instance. |
| **Enforcement** | `SessionFactories.GetOrAdd(options.ConnectionString, _ => BuildSessionFactory(options))` under a global lock. |
| **Outcome** | Two named options sharing a connection string share one session factory, and therefore share whichever `BatchSize`/`ShowSql`/`DriverFormatSql`/mapping set was captured when the factory was first built. |
| **Source** | `src/SAIF.NetCore.Nhibernate/SAIF.NetCore.NHibernate/SAIF.NetCore.NHibernate/Factories/UnitOfWorkFactory.cs` |

---

## BR-NH-06 INhConfiguration wrapper cached by name but wrapped NHibernate Configuration rebuilt per cache miss

| Field | Value |
| --- | --- |
| **Statement** | `UnitOfWorkFactory` caches the `INhConfiguration` wrapper per factory instance, keyed by the options `Name`, separately from the `SessionFactories` cache; the NHibernate `Configuration` object that wrapper produces is not itself cached — it is rebuilt every time `BuildSessionFactory` runs for an outer `SessionFactories` cache miss. |
| **Trigger** | `UnitOfWorkFactory.BuildSessionFactory` / `CreateConfiguration` run for a given options name, invoked whenever `GetUnitOfWork` misses the `SessionFactories` cache for that options' connection string. |
| **Enforcement** | `Configuration.GetOrAdd(options.Name, _ => CreateConfiguration(options))` under a lock on the instance-level `Configuration` dictionary returns the same `INhConfiguration` wrapper for a given name, but `BuildSessionFactory` then calls `config.Configure(this.Mappings)` unconditionally; `BaseConfiguration.Configure` calls `GetConfiguration()`, which always runs `Fluently.Configure().Database(ConfigureDriver()).Mappings(mappingBuilder)` followed by `.BuildConfiguration()` and assigns the result to a new `Configuration` object before re-applying `ConnectionString`/`BatchSize`/`ShowSql`. |
| **Outcome** | The `INhConfiguration` wrapper instance (and the engine selection it was constructed with) is built once per name per factory instance and reused on subsequent calls with the same name, but the underlying NHibernate `Configuration` object it wraps is rebuilt from scratch on every `SessionFactories` cache miss — the wrapper cache hitting does not mean the inner NHibernate `Configuration` is cached too. |
| **Source** | `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` |

---

## BR-NH-07 Unrecognized database type falls back to Oracle

| Field | Value |
| --- | --- |
| **Statement** | An out-of-range `DatabaseType` value falls back to Oracle for both the engine configuration and the session helper, with no error raised. |
| **Trigger** | `options.Database` holds a value outside the `Oracle`/`Sql`/`PostGreSql` enum range. |
| **Enforcement** | The `switch` in `CreateConfiguration` and in `BuildSessionHelper` both use a `default` arm that selects the Oracle path. |
| **Outcome** | Misconfigured or corrupted `Database` values silently produce an Oracle session rather than failing fast. |
| **Tables** | [DUAL](database-tables.md#dual) |
| **SQL** | [SQL-001](sql/SQL-001-oracle-system-date.md) |
| **Source** | `src/SAIF.NetCore.Nhibernate/SAIF.NetCore.NHibernate/SAIF.NetCore.NHibernate/Factories/UnitOfWorkFactory.cs` |

---

## BR-NH-08 Fluent and HBM mappings both loaded

| Field | Value |
| --- | --- |
| **Statement** | Both FluentNHibernate `ClassMap<T>` mappings and embedded NHibernate HBM (`*.hbm.xml`) mappings are loaded from every registered assembly. |
| **Trigger** | `BaseConfiguration.GetConfiguration()` builds the FluentNHibernate configuration. |
| **Enforcement** | For each assembly: `FluentMappings.AddFromAssembly(a)` **and** `HbmMappings.AddFromAssembly(a)` are both called. |
| **Outcome** | A consuming application can mix Fluent and legacy HBM mapping styles within the same mapping assembly set. |
| **SQL** | [SQL-004](sql/SQL-004-test-connection.md) |
| **Source** | `src/SAIF.NetCore.Nhibernate/SAIF.NetCore.NHibernate/SAIF.NetCore.NHibernate/Factories/Configuration/BaseConfiguration.cs` |

---

## BR-NH-09 SQL debug interceptor applies only to stateful sessions while debugging

| Field | Value |
| --- | --- |
| **Statement** | The SQL debug interceptor is attached only to stateful sessions when `debugOutputSql = true`, and it emits output only while a debugger is attached. |
| **Trigger** | `UnitOfWorkFactory.BuildSessionStrategy` opens a stateful session with `debugOutputSql` set. |
| **Enforcement** | `factory.WithOptions().Interceptor(new SqlDebugOutputInterceptor()).OpenSession()` for the stateful + debug path; `SqlDebugOutputInterceptor.OnPrepareStatement` checks `Debugger.IsAttached` before writing to `Trace`. |
| **Outcome** | Stateless sessions never receive SQL tracing regardless of `debugOutputSql`; stateful sessions only trace when a debugger is actually attached to the process. |
| **Source** | `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/SqlDebugOutputInterceptor.cs` |

---

## BR-NH-10 Session type selected by StatelessSessionStrategy option

| Field | Value |
| --- | --- |
| **Statement** | The session strategy (stateful vs. stateless) is chosen per unit of work by `UnitOfWorkOptions.StatelessSessionStrategy`, which defaults to `false` (stateful). |
| **Trigger** | `UnitOfWorkFactory.BuildSessionStrategy` runs for every `GetUnitOfWork` call. |
| **Enforcement** | A boolean check on `options.StatelessSessionStrategy` selects `StatelessSessionStrategy` or `StatefulSessionStrategy`. |
| **Outcome** | Consumers get change-tracked stateful sessions unless they explicitly opt into stateless, bulk-throughput sessions. |
| **Source** | `src/SAIF.NetCore.Nhibernate/SAIF.NetCore.NHibernate/SAIF.NetCore.NHibernate/Factories/UnitOfWorkFactory.cs`, `src/SAIF.NetCore.Nhibernate/SAIF.NetCore.NHibernate/SAIF.NetCore.NHibernate/Options/UnitOfWorkOptions.cs` |

---

## BR-NH-11 Transactions always use ReadCommitted isolation

| Field | Value |
| --- | --- |
| **Statement** | `UnitOfWork.Begin` always opens a transaction at `ReadCommitted` isolation; the stateful strategy ignores any isolation level argument it is given. |
| **Trigger** | `UnitOfWork.Begin()` is called. |
| **Enforcement** | `Begin` hard-codes `IsolationLevel.ReadCommitted`; `StatefulSessionStrategy.BeginTransaction` ignores its own isolation-level parameter and also always uses `ReadCommitted`. |
| **Outcome** | No consumer calling `UnitOfWork.Begin()` — the standard entry point — can obtain a different isolation level, because `Begin` hard-codes the argument and the stateful strategy ignores it regardless. This does not extend to every possible call path: the **stateless** strategy's `BeginTransaction(isolationLevel)` honors whatever level is passed to it directly (`session.BeginTransaction(isolationLevel)`), so a consumer calling `ISessionStrategy.BeginTransaction` directly on a stateless session, bypassing `UnitOfWork.Begin()`, can select a different isolation level. |
| **SQL** | [SQL-004](sql/SQL-004-test-connection.md) |
| **Source** | `src/SAIF.NetCore.Nhibernate/SAIF.NetCore.NHibernate/SAIF.NetCore.NHibernate/UnitOfWork.cs`, `src/SAIF.NetCore.Nhibernate/SAIF.NetCore.NHibernate/SAIF.NetCore.NHibernate/SessionStrategies/StatefulSessionStrategy.cs` |

---

## BR-NH-12 Begin refused while a transaction is active

| Field | Value |
| --- | --- |
| **Statement** | `Begin` throws `InvalidOperationException("There is a transaction already in progress.")` when the session's current transaction is still active (begun and not yet committed or rolled back). It does not limit a unit of work to one transaction. |
| **Trigger** | `UnitOfWork.Begin()` is called while a transaction started by an earlier `Begin()` has not yet been committed or rolled back. |
| **Enforcement** | `IsTransactionInProgress()` reads `Session.Transaction()` on every call and checks `IsActive \|\| WasCommitted \|\| WasRolledBack` on that transaction. Against NHibernate 5.2.7 (resolved transitively via FluentNHibernate 3.0.1), a successful `Commit` or a `Rollback` detaches the completed transaction from the session (`ConnectionManager.AfterTransaction()` sets `_transaction = null`), and the next `Session.Transaction` read returns a new, un-begun transaction. In practice the guard therefore rejects only an active transaction; the `WasCommitted`/`WasRolledBack` checks do not fire on that standard path. `Session.Transaction` never returns `null` in NHibernate 5.2.7. |
| **Outcome** | A second `Begin()` while a transaction is active throws. After a successful `Commit()` the session stays open, and a further `Begin()` on the same unit of work starts a new transaction instead of throwing. Using a new unit of work for each transaction is recommended, but the library does not enforce it. |
| **SQL** | [SQL-004](sql/SQL-004-test-connection.md) |
| **Source** | `src/SAIF.NetCore.Nhibernate/SAIF.NetCore.NHibernate/SAIF.NetCore.NHibernate/UnitOfWork.cs`, NHibernate 5.2.7 source: `src/NHibernate/AdoNet/ConnectionManager.cs` (`AfterTransaction`, `Transaction`), `src/NHibernate/Transaction/AdoTransaction.cs` (`Commit`, `AfterTransactionCompletion`) |

---

## BR-NH-13 Commit requires an active transaction

| Field | Value |
| --- | --- |
| **Statement** | `Commit` throws `InvalidOperationException` if no transaction is active; a successful commit also calls `Session.Clear()`, which clears the first-level cache on the stateful strategy but is a no-op on the stateless strategy (which has no first-level cache and already writes changes immediately). |
| **Trigger** | `UnitOfWork.Commit()` is called. |
| **Enforcement** | A guard checks for an active transaction before committing; on success, `Session.Clear()` runs after the commit (stateful: discards cache; stateless: no-op). |
| **Outcome** | Callers must call `Begin()` before `Commit()`; on the stateful strategy, committed state is not retained in the session's identity map afterward. On the stateless strategy, there is no identity map to retain state in, and `Commit()` is effectively a COMMIT-only operation on an already-immediately-written set of changes. |
| **SQL** | [SQL-004](sql/SQL-004-test-connection.md) |
| **Source** | `src/SAIF.NetCore.Nhibernate/SAIF.NetCore.NHibernate/SAIF.NetCore.NHibernate/UnitOfWork.cs` |

---

## BR-NH-14 Rollback disposes the session only when rollback itself does not throw

| Field | Value |
| --- | --- |
| **Statement** | `Rollback` rolls back the active transaction, if any, and then **in the normal (non-throwing) case** disposes the session, regardless of whether a transaction was active. |
| **Trigger** | `UnitOfWork.Rollback()` is called. |
| **Enforcement** | The method conditionally rolls back, then disposes the session — but there is no `try`/`finally` around the rollback: if `transaction.Rollback()` itself throws, `Session.Dispose()` on the next line is skipped entirely. `Rollback()` also does not set the `isDisposed` flag, so a later `Dispose()` call still runs full disposal. |
| **Outcome** | When `transaction.Rollback()` does not throw, the session is disposed. Whether a unit of work can still be used afterward is not fully characterized by this rule alone: the stateful session strategy can transparently reopen a closed session on next access (see [ISessionStrategy](api/SAIF.NetCore.NHibernate.Interfaces/ISessionStrategy.md)), so "disposed" does not by itself prove every later call fails. When `transaction.Rollback()` throws, session disposal is skipped and the exception propagates; neither a left-open session nor a specific failure mode for subsequent calls is proven by this library's own source in that case.** |
| **Source** | `src/SAIF.NetCore.Nhibernate/SAIF.NetCore.NHibernate/SAIF.NetCore.NHibernate/UnitOfWork.cs` |

---

## BR-NH-15 Add Update and Load vary by session strategy

| Field | Value |
| --- | --- |
| **Statement** | Repository CRUD semantics differ by session strategy: stateful `Add` upserts via `SaveOrUpdate`, stateless `Add` always inserts via `Insert`; stateful `Load` calls `ISession.Load`, stateless `Load` calls `IStatelessSession.Get`. `ISession.Load` returns a lazy, no-round-trip proxy only when the entity is mapped with a proxy — a consumer mapping choice this library does not configure; with no proxy mapping, `Load` behaves like an eager fetch. |
| **Trigger** | `Repository<TEntity>.Add(entity)` or `Repository<TEntity>.Load(id)` is called. |
| **Enforcement** | `StatefulSessionStrategy` and `StatelessSessionStrategy` map each `IRepository<TEntity>` member to different underlying `ISession`/`IStatelessSession` calls. |
| **Outcome** | Consumers switching `StatelessSessionStrategy` on an existing repository usage must account for upsert-vs-insert differences, and must not assume stateful `Load` is always a deferred, no-round-trip operation: that outcome depends on the consumer's entity mapping, not on this library. |
| **Source** | `src/SAIF.NetCore.Nhibernate/SAIF.NetCore.NHibernate/SAIF.NetCore.NHibernate/SessionStrategies/StatefulSessionStrategy.cs`, `src/SAIF.NetCore.Nhibernate/SAIF.NetCore.NHibernate/SAIF.NetCore.NHibernate/SessionStrategies/StatelessSessionStrategy.cs` |

---

## BR-NH-16 Legacy registration API is a compile-time error

| Field | Value |
| --- | --- |
| **Statement** | Calls to the legacy pre-.NET Core 3.1 registration API (`IServicesNhibernateExtensions`, 13 methods) are compile-time errors; every member is a no-op that returns `services` unchanged. |
| **Trigger** | Consumer code calls any of `AddUnitOfWorkAccessor`, `UseOracleSession`, `UsePostGreSqlSession`, `UseSqlSession`, `AddOracleUnitOfWork`, `AddSqlUnitOfWork`, or `AddPostGreSqlUnitOfWork`. |
| **Enforcement** | `[Obsolete("Due to functional changes between .net core 2.2 and .net core 3.1, this method is obsolete to avoid startup errors.", true)]` on every member of `IServicesNhibernateExtensions`. |
| **Outcome** | Consumers are forced to migrate to `AddNhibernateServices`/`ConfigureUnitOfWorkOptions`; the checked-in `README.md` still documents the obsolete API and should not be treated as current.* |
| **Source** | `src/SAIF.NetCore.Nhibernate/SAIF.NetCore.NHibernate/SAIF.NetCore.NHibernate/Extensions/IServicesNhibernateExtensions.cs` |

---

## BR-NH-17 SystemDate comes from the database server

| Field | Value |
| --- | --- |
| **Statement** | `SystemDate` is always obtained from the database server through an engine-specific query, never from the application server clock under normal operation. |
| **Trigger** | `ISessionHelper.SystemDate` / `IRepository<T>.SystemDate` is read. |
| **Enforcement** | `SessionHelperBase.SystemDate` runs `Session.CreateSqlQuery(DateSql).UniqueResult()` on every access; `DateSql` is set per engine by the concrete `*SessionHelper` subclass. |
| **Outcome** | Audit timestamps built from `SystemDate` reflect database server time, not application server time, as long as the query succeeds. On PostgreSQL, the returned value is date-only (midnight, no time-of-day component) — unlike Oracle's `sysdate`/SQL Server's `GetDate()`, which both return full date+time — so `SystemDate` on PostgreSQL is unsuitable for audit timestamps that need time-of-day precision. |
| **Tables** | [DUAL](database-tables.md#dual) |
| **SQL** | [SQL-001](sql/SQL-001-oracle-system-date.md), [SQL-002](sql/SQL-002-postgresql-system-date.md), [SQL-003](sql/SQL-003-sql-server-system-date.md) |
| **Source** | `src/SAIF.NetCore.Nhibernate/SAIF.NetCore.NHibernate/SAIF.NetCore.NHibernate/SessionHelpers/SessionHelperBase.cs` |

---

## BR-NH-18 SystemDate falls back to local time on sentinel values

| Field | Value |
| --- | --- |
| **Statement** | If the database system-date query returns `DateTime.MinValue` or `DateTime.MaxValue`, `SystemDate` falls back to the application server's local `DateTime.Now`. |
| **Trigger** | The system-date query result converts to one of the two sentinel `DateTime` values. |
| **Enforcement** | `SessionHelperBase.SystemDate` compares the converted result to `DateTime.MinValue`/`DateTime.MaxValue` before returning. |
| **Outcome** | A successfully converted result equal to `DateTime.MinValue`/`DateTime.MaxValue` silently substitutes local server time rather than surfacing an error. This fallback is narrow: a failed `Convert.ToDateTime` or a query/connectivity exception is **not** caught anywhere in `SystemDate` and propagates to the caller unhandled — it is not masked by this fallback. |
| **SQL** | [SQL-001](sql/SQL-001-oracle-system-date.md), [SQL-002](sql/SQL-002-postgresql-system-date.md), [SQL-003](sql/SQL-003-sql-server-system-date.md) |
| **Source** | `src/SAIF.NetCore.Nhibernate/SAIF.NetCore.NHibernate/SAIF.NetCore.NHibernate/SessionHelpers/SessionHelperBase.cs` |

---

## BR-NH-19 UnitOfWorkOptions default values

| Field | Value |
| --- | --- |
| **Statement** | `UnitOfWorkOptions`'s constructor sets non-trivial defaults: `BatchSize = 20`, `ShowSql = true`, `DriverFormatSql = true`, `StatelessSessionStrategy = false`, `Name = "default"`, `Database = Oracle`. |
| **Trigger** | A new `UnitOfWorkOptions` instance is created. Under the .NET Options framework, `IOptionsSnapshot<UnitOfWorkOptions>.Get(name)` resolves (and re-runs all matching `Configure` calls for) a named options instance once per name per DI scope — it is cached and reused for the rest of that scope's lifetime, not reconstructed on every single `Get(name)` call within the same scope. This options-object lifecycle is separate from `UnitOfWork`/NHibernate session creation: `UnitOfWorkFactory.CreateUnitOfWork`/`CreateSessionUnitOfWork` always construct and return a brand-new `UnitOfWork` instance on every `GetUnitOfWork(options)` call, regardless of options caching. |
| **Enforcement** | The `UnitOfWorkOptions` constructor assigns each property its literal default. |
| **Outcome** | Unless a consumer overrides them, every database connection logs and pretty-prints all generated SQL (`ShowSql`/`DriverFormatSql` both `true`), even in production. |
| **SQL** | [SQL-001](sql/SQL-001-oracle-system-date.md) |
| **Source** | `src/SAIF.NetCore.Nhibernate/SAIF.NetCore.NHibernate/SAIF.NetCore.NHibernate/Options/UnitOfWorkOptions.cs` |

---

## Notes
- This library enforces no domain-specific business rules; all rules above are data-access infrastructure rules that every consuming application inherits by using the library's default behavior.

## Related documentation
- [Index](index.md)
- [Overview](overview.md)
- [Architecture](architecture.md)
- [Dependencies](dependencies.md)
- [Configuration](configuration.md)
- [Operations and processes](operations-processes.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/Extensions/IServicesCollectionExtensions.cs`
- `src/SAIF.NetCore.Nhibernate/SAIF.NetCore.NHibernate/SAIF.NetCore.NHibernate/Extensions/IServicesNhibernateExtensions.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/Factories/Configuration/SqlDebugOutputInterceptor.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/UnitOfWorkOptions.cs`
- `src/SAIF.NetCore.Nhibernate/SAIF.NetCore.NHibernate/SAIF.NetCore.NHibernate/UnitOfWork.cs`
- `src/SAIF.NetCore.Nhibernate/SAIF.NetCore.NHibernate/SAIF.NetCore.NHibernate/SessionStrategies/StatefulSessionStrategy.cs`
- `src/SAIF.NetCore.Nhibernate/SAIF.NetCore.NHibernate/SAIF.NetCore.NHibernate/SessionStrategies/StatelessSessionStrategy.cs`
- `src/SAIF.NetCore.Nhibernate/SAIF.NetCore.NHibernate/SAIF.NetCore.NHibernate/SessionHelpers/SessionHelperBase.cs`
- NHibernate 5.2.7 (resolved via FluentNHibernate 3.0.1): `src/NHibernate/AdoNet/ConnectionManager.cs` (`AfterTransaction`, `Transaction`), `src/NHibernate/Transaction/AdoTransaction.cs` (`Commit`, `AfterTransactionCompletion`)
