SAIF.NetCore.NHibernate architecture¶
SAIF.NetCore.NHibernate is the repository's NHibernate data-access infrastructure provider: it defines a IUnitOfWork/IUnitOfWorkFactory/IRepository<TEntity>/ISessionStrategy stack, a named-options configuration model, and per-engine driver configurations for Oracle, SQL Server, and PostgreSQL, through FluentNHibernate — but it ships no entity mappings or business schema of its own, leaving that to consuming applications.
Responsibilities¶
- Define the unit-of-work and repository abstractions (
IUnitOfWork,IUnitOfWorkFactory,IRepository<TEntity>,ISessionStrategy,ISessionHelper) that consuming applications build entity access on top of. - Build and cache an NHibernate
ISessionFactoryper connection string (UnitOfWorkFactory), selecting the Oracle, SQL Server, or PostgreSQL driver configuration fromUnitOfWorkOptions.Database. - Provide stateful and stateless session strategy implementations (
StatefulSessionStrategy,StatelessSessionStrategy) with matching engine-specificSessionHelperimplementations for a trivial system-date query. - Supply a named-options configuration pattern (
UnitOfWorkOptions,ConfigureUnitOfWorkOptions) through which a consuming application reads connection strings and environment settings viaIEnvironmentClient/IDBConnectionClient. - Register the modern DI surface (
AddNhibernateServices) while retaining 13 legacy registration methods as[Obsolete("Due to functional changes between .net core 2.2 and .net core 3.1, this method is obsolete to avoid startup errors.", true)]compile-error stubs that force migration off the pre-.NET Core 3.1 API.
Component boundaries¶
UnitOfWorkFactory/UnitOfWork: lifecycle — builds/caches theISessionFactoryper connection string, begins/commits/rolls back the unit of work, and hands outRepository<TEntity>instances.UnitOfWork's constructor isinternal, so onlyUnitOfWorkFactorycan create one.Repository<TEntity>(implementsIRepository<TEntity>): the CRUD/query facade over a concreteUnitOfWork, exposingAdd/Update/Delete/Get/Load/Query().ISessionStrategyimplementations (StatefulSessionStrategy,StatelessSessionStrategy): wrap NHibernate'sISession/IStatelessSession, the only point where consumers can reach the raw NHibernate session.ISessionHelperimplementations (OracleSessionHelper,SqlServerSessionHelper,PostgreSqlSessionHelper): engine-specific system-date queries used bySessionHelperBase.SystemDate.BaseConfigurationand its engine subclasses (OracleConfiguration,SqlConfiguration,PostGreSqlConfiguration): FluentNHibernate driver/dialect configuration perDatabaseType, plus optional SQL debug output viaSqlDebugOutputInterceptor.UnitOfWorkOptions/ConfigureUnitOfWorkOptions(abstract): the named-options POCO and the pattern a consuming application subclasses to resolve its connection string and engine fromIEnvironmentClient/IDBConnectionClient.IServicesCollectionExtensions(AddNhibernateServices): the current DI registration surface.IServicesNhibernateExtensions: 13 legacy registration methods, each marked[Obsolete]with its error flag set totrue, so calling any of them is a compile-time error — they are retained in the public API surface solely as compile-time-error migration diagnostics, not as working source-compatible registration methods, and must not be called.- Contract boundary:
IEnvironmentClientandIDBConnectionClientare declared in SAIF.NetCore.ServiceContracts; this library only consumes them as constructor-injected fields on a consumer-authoredConfigureUnitOfWorkOptionssubclass.
Interaction model¶
- A consuming application calls
AddNhibernateServices(assemblies, debugOutputSql: false)(the non-generic overload requiresdebugOutputSql) or the genericAddNhibernateServices<T>()to registerIUnitOfWorkFactoryas a singleton andUnitOfWorkAccessoras a scoped service — nothing else. - As a separate host registration, the consuming application must also register its own
ConfigureUnitOfWorkOptionssubclass, typically viaservices.ConfigureOptions<TConfigurer>();AddNhibernateServicesdoes not register this configurer or anyIConfigureOptions<UnitOfWorkOptions>on the consumer's behalf. - That subclass is constructed with
IEnvironmentClientandIDBConnectionClient(both from SAIF.NetCore.ServiceContracts) and implementsGetConnectionString()by calling the appropriateIDBConnectionClientmethod (GetOracleConnection/GetSqlServerConnection/GetPostgreSqlServerConnection) for its configuredDatabaseType. - At request time, the application resolves
IUnitOfWorkFactory(directly, or throughUnitOfWorkAccessor.UnitOfWork(key)) and callsGetUnitOfWork(options);UnitOfWorkFactorybuilds (or reuses a cached)ISessionFactorykeyed by connection string, selects the stateful or stateless session strategy, and returns anIUnitOfWork. - The application then calls
GetRepository<TEntity>()on the unit of work to obtain aRepository<TEntity>bound to its ownClassMap<TEntity>entity mappings — which this library does not supply — and issuesAdd/Update/Delete/Get/Load/Query()calls against it. - An unrecognized
DatabaseTypevalue silently falls back to Oracle for both the driver configuration and the session helper*, rather than failing fast. - No SAIF library in this repository consumes this package:
SAIF.NetCore.ServiceExtensionsdoes not reference it, even though the top-level architecture diagram draws a composition arrow from ServiceExtensions to the data group — applications must reference SAIF.NetCore.NHibernate directly*.
Architecture diagram¶
The following diagram shows how a consuming application's unit-of-work options drive session-factory construction and entity access.
flowchart TD
A["Consuming app:<br>ConfigureUnitOfWorkOptions subclass"] --> B["IEnvironmentClient /<br>IDBConnectionClient"]
B --> C["UnitOfWorkOptions:<br>Database, ConnectionString"]
C --> D[UnitOfWorkFactory.GetUnitOfWork]
D --> E{"ISessionFactory cached<br>for connection string?"}
E -->|no| F["BaseConfiguration subclass:<br>Oracle / Sql / PostGreSql driver"]
F --> G["FluentNHibernate:<br>BuildSessionFactory"]
E -->|yes| H[Reuse cached ISessionFactory]
G --> I["SessionStrategy:<br>Stateful or Stateless"]
H --> I
I --> J["UnitOfWork"]
J --> K["Repository<TEntity>:<br>consumer-supplied ClassMap"]
Sequence diagram¶
The following diagram shows the primary request/response sequence for obtaining a unit of work and querying an entity.
sequenceDiagram
participant App as "Consuming application"
participant Factory as UnitOfWorkFactory
participant Config as "ConfigureUnitOfWorkOptions"
participant NH as "NHibernate / FluentNHibernate"
participant Repo as "Repository#lt;TEntity#gt;"
App->>Config: GetConnectionString() via IDBConnectionClient
App->>Factory: GetUnitOfWork(options)
Factory->>NH: BuildSessionFactory (cached by connection string)
NH-->>Factory: ISessionFactory
Factory-->>App: IUnitOfWork
App->>Repo: unitOfWork.GetRepository<TEntity>()
App->>Repo: Query() / Get(id) / Add(entity)
Repo->>NH: Execute against ISession/IStatelessSession
NH-->>Repo: Result
Repo-->>App: Result
Design constraints¶
- The library ships no entity mappings or business schema; the only concrete SQL it owns is three engine-specific
CreateSqlQuerysystem-date statements and one generic LINQ call site (UnitOfWork.TestConnection<TEntity>()). ISessionStrategyexposes the raw NHibernateISession/IStatelessSession, so consumers can bypass the repository abstraction entirely; it also declaresDispose()without implementingIDisposable.- 13 legacy registration methods in
IServicesNhibernateExtensionsare[Obsolete("Due to functional changes between .net core 2.2 and .net core 3.1, this method is obsolete to avoid startup errors.", true)]no-ops; the checked-inREADME.mdstill documents this stale API and should not be treated as current. - The static
SessionFactoriescache is keyed only by connection string, so twoConfigureUnitOfWorkOptionswith the same connection string but different mapping assemblies would collide on cache lookup — unconfirmed in practice**. - NHibernate's own resolved version is not pinned in this repository; it arrives transitively through FluentNHibernate 3.0.1**.
Related documentation¶
- Index
- Overview
- Dependencies
- Configuration
- Operations and processes
- Business rules
- Database tables
- SQL catalog
- Service registration
- API reference
- SAIF.NetCore.ServiceContracts overview
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/SAIF.NetCore.NHibernate.csprojsrc/SAIF.NetCore.Nhibernate/SAIF.NetCore.NHibernate/SAIF.NetCore.NHibernate/Factories/UnitOfWorkFactory.cssrc/SAIF.NetCore.Nhibernate/SAIF.NetCore.NHibernate/SAIF.NetCore.NHibernate/Repository.cssrc/SAIF.NetCore.Nhibernate/SAIF.NetCore.NHibernate/SAIF.NetCore.NHibernate/UnitOfWork.cssrc/SAIF.NetCore.Nhibernate/SAIF.NetCore.NHibernate/SAIF.NetCore.NHibernate/Options/ConfigureUnitOfWorkOptions.cssrc/SAIF.NetCore.Nhibernate/SAIF.NetCore.NHibernate/SAIF.NetCore.NHibernate/Extensions/IServicesCollectionExtensions.csArchitecture.md