SAIF.Framework.Data.NHibernate architecture¶
SAIF.Framework.Data.NHibernate is a layered NHibernate ORM data-access library that implements and consumes — rather than defines — the unit-of-work/repository/session seam used by consuming applications: engine-specific configuration/session factories, session-strategy adapters for stateful and stateless NHibernate sessions, a transaction-scoped unit of work, generic CRUD repositories, and 42 FluentNHibernate entity mappings across 10 domain areas. The seam contracts (IUnitOfWork, IRepository<TEntity>, ISessionHelper, IDataEntity) are supplied by the SAIF.Framework.Core package dependency, namespace SAIF.Data — an ordinary, forward-direction dependency, the same direction as this library's dependency on NHibernate — brought into scope via C# enclosing-namespace lookup (this project's RootNamespace, SAIF.Data.NHibernate, nests under Core's SAIF.Data) rather than an explicit using SAIF.Data; directive. This project's own SAIF.Data.NHibernate.Interfaces namespace is unrelated and holds only its 3 locally-declared interfaces (IConfigurationFactory, ISessionStrategy, IUnitOfWorkFactory).
Responsibilities¶
- Build and cache engine-specific NHibernate
Configuration/ISessionFactoryinstances (Oracle and SQL Server), keyed by connection string, viaFactories/ConfigurationFactoryBaseand itsOracleConfigurationFactory/SqlServerConfigurationFactorysubclasses. - Construct
NHibernateUnitOfWorkinstances that wrap either a stateful (ISession) or stateless (IStatelessSession) NHibernate session behind a commonISessionStrategyseam, viaFactories/UnitOfWorkFactoryBaseand itsOracleUnitOfWorkFactory/OracleStatelessUnitOfWorkFactory/SqlServerUnitOfWorkFactorysubclasses. - Own the transaction boundary (
Begin()/Commit()/Rollback()) for all repositories obtained from a given unit of work (UnitOfWork/NHibernateUnitOfWork). - Provide a generic CRUD/query surface (
Add/Update/Delete/Query/Get/Load/SystemDate) over either session strategy (Repositories/RepositoryBase<TEntity>,Repositories/Repository<TEntity>). - Supply engine-specific server-time retrieval with a local-clock fallback (
SessionHelper/SessionHelperBaseand itsOracleSessionHelper/SqlServerSessionHelpersubclasses). - Declaratively map 42 entities from the
SAIF.Data.Model.*namespaces to their relational schema via FluentNHibernateClassMap<TEntity>classes (Mappings/), discovered through assembly scanning rather than direct external consumption. - Provide one domain-specific extension (
Repositories/Imaging/ImageRepositoryExtensions) that generates Oracle sequence-based IDs for the Imaging domain via a direct SQL escape hatch.
Component boundaries¶
- Interfaces:
IConfigurationFactory,ISessionStrategy,IUnitOfWorkFactory— the three seam contracts declared and owned by this project, in its ownSAIF.Data.NHibernate.Interfacesnamespace, that decouple database-engine-specific code from the repository/unit-of-work API; this is the public API surface consumers use when wiring up dependency injection. In addition to these three locally-declared interfaces, the library's classes implement four seam contracts owned by the upstreamSAIF.Framework.Corepackage dependency, namespaceSAIF.Data(brought into scope via C# enclosing-namespace lookup, not via theSAIF.Data.NHibernate.Interfacesnamespace above) —IUnitOfWork(implemented byNHibernateUnitOfWork),IRepository<TEntity>(implemented byRepositoryBase<TEntity>),ISessionHelper(implemented bySessionHelperBase), andIDataEntity(used as a generic constraint onGetRepository<TEntity>()); see Dependencies for theSAIF.Framework.Corepackage-dependency evidence. - Factories:
ConfigurationFactoryBase/OracleConfigurationFactory/SqlServerConfigurationFactorybuild and cache NHibernateConfiguration/ISessionFactoryobjects (ConcurrentDictionary<string, PersistenceConfiguration>andConcurrentDictionary<string, ISessionFactory>keyed by connection string);UnitOfWorkFactoryBase/OracleUnitOfWorkFactory/OracleStatelessUnitOfWorkFactory/SqlServerUnitOfWorkFactorybuildNHibernateUnitOfWorkinstances per engine/session-strategy combination. This is the only layer that touchesFluently.Configure(),OracleDataClientConfiguration, orMsSqlConfiguration. - Sessions:
StatefulSessionStrategy/StatelessSessionStrategy(both internal) implementISessionStrategyover a real NHibernateISession/IStatelessSession, translatingAdd/Update/Delete/Get/Load/Queryinto the correct call for each session type. These classes encapsulate the stateful-vs-stateless semantics divergence and are never exposed directly outside the assembly. - SessionHelper:
SessionHelperBase/OracleSessionHelper/SqlServerSessionHelper(all internal) exposeSystemDatevia an engine-specific SQL string, falling back toDateTime.Nowon a null/min/max result. This is an infrastructure utility consumed byRepositoryBase<TEntity>.SystemDate, not a business rule. - UnitOfWork:
NHibernateUnitOfWorkis the transaction boundary.Begin()/Commit()/Rollback()wrap the NHibernateITransaction;GetRepository<TEntity>()hands out aRepository<TEntity>bound to this unit of work's session, so all repositories obtained from one instance share its transaction. - Repositories:
RepositoryBase<TEntity>/Repository<TEntity>provide a generic CRUD + LINQ query surface overISessionStrategy;Repositories/Imaging/ImageRepositoryExtensionsadds three Oracle sequence-based ID-generation extension methods scoped to the Imaging domain — the one custom-repository-style extension found in this library. - Mappings: 42
ClassMap<TEntity>classes across 10 domain sub-folders (Contacts,Deployment,Employee,Imaging,Menu,PDF,Preferences,Printing,Users,WebContext), mirroring theSAIF.Data.Model.*namespace structure. The mapping is the declarative persistence contract — no inline CRUD SQL or business-criteria logic exists for these entities.
Interaction model¶
- A consumer obtains an
IUnitOfWorkFactoryimplementation (OracleUnitOfWorkFactory,OracleStatelessUnitOfWorkFactory, orSqlServerUnitOfWorkFactory), constructed with the matchingIConfigurationFactory. GetUnitOfWork(connectionString, batchSize, showSql)resolves (or builds and caches) anISessionFactoryfor that connection string viaConfigurationFactory.GetConfiguration(connection, batchSize, showSql)andBuildSessionFactory().- The factory opens an
ISession(CreateSessionUnitOfWork) orIStatelessSession(CreateStatelessSessionUnitOfWork), wraps it inStatefulSessionStrategy/StatelessSessionStrategy, builds the matchingISessionHelper(OracleSessionHelper/SqlServerSessionHelper), and returns aNHibernateUnitOfWork. - The consumer calls
Begin()to clear the session and start aReadCommittedtransaction, callsGetRepository<TEntity>()one or more times to obtainRepository<TEntity>instances sharing that unit of work's session, performsAdd/Update/Delete/Query/Get/Loadoperations, then callsCommit()orRollback(). - Repository operations diverge by session strategy:
Update()is a no-op under the stateful strategy (relying on NHibernate's automatic dirty-checking at commit) but performs an explicitIStatelessSession.Update(entity)call under the stateless strategy;Get/Loadreturn a lazy proxy under the stateful strategy but both delegate to a non-lazyIStatelessSession.Get<TEntity>(id)under the stateless strategy. ImageRepositoryExtensionsbypasses the mapped-entity path entirely for ID generation: it castsIRepository<T>to the concreteRepository<T>to reach the underlyingSessionand issues a directCreateSqlQuery("select {sequence}.nextval from dual")call — the one direct-SQL escape hatch in this library.
Architecture diagram¶
The following diagram shows the high-level component interactions across the layered NHibernate stack.
flowchart TD
Consumer["Consuming application"] --> IUoWF["IUnitOfWorkFactory<br>(Oracle / OracleStateless / SqlServer)"]
IUoWF --> ICF["IConfigurationFactory<br>(Oracle / SqlServer)"]
ICF --> Mappings["Mappings/<br>42 ClassMap<TEntity> (assembly scan)"]
IUoWF --> NUoW["NHibernateUnitOfWork"]
NUoW --> ISS["ISessionStrategy<br>(Stateful / Stateless)"]
NUoW --> ISH["ISessionHelper<br>(Oracle / SqlServer)"]
NUoW --> Repo["Repository<TEntity><br>(RepositoryBase<TEntity>)"]
Repo --> ISS
Repo --> ImgExt["ImageRepositoryExtensions<br>(Oracle sequence NEXTVAL)"]
ISS --> NH["NHibernate ISession / IStatelessSession"]
Sequence diagram¶
The following diagram shows the primary unit-of-work/repository request sequence for a transactional operation.
sequenceDiagram
participant Consumer
participant Factory as IUnitOfWorkFactory
participant UoW as NHibernateUnitOfWork
participant Repo as "Repository(TEntity)"
participant Session as ISessionStrategy
Consumer->>Factory: GetUnitOfWork(connectionString, batchSize, showSql)
Factory-->>Consumer: NHibernateUnitOfWork
Consumer->>UoW: Begin()
UoW->>Session: Clear() / BeginTransaction(ReadCommitted)
Consumer->>UoW: GetRepository(TEntity)()
UoW-->>Consumer: Repository(TEntity)
Consumer->>Repo: Add/Update/Delete/Query/Get/Load
Repo->>Session: delegate call
Session-->>Repo: result
Consumer->>UoW: Commit()
UoW->>Session: transaction.Commit() / Session.Clear()
Design constraints¶
Begin()enforces that no transaction is already active (IsTransactionInProgress()check) and throwsInvalidOperationExceptionotherwise;Commit()enforces thatBegin()was called first and throwsInvalidOperationExceptionotherwise — both are structural invariants rather than domain business rules.Rollback()disposes the session unconditionally, even if no transaction was active.- A
Repository<TEntity>is scoped to exactly oneNHibernateUnitOfWorkinstance's session for its entire lifetime; repositories are not reusable across units of work, so all repositories obtained from the same unit-of-work instance participate in the same transaction. - Deserialization of a previously-serialized
Configuration(BinaryFormatterreading{assemblyDirectory}\{Oracle|SqlServer}.config) silently swallows any exception and falls back to building a fresh configuration, rather than surfacing the deserialization failure. SystemDatefalls back to localDateTime.Nowif the underlying database query returns null,DateTime.MinValue, orDateTime.MaxValue, so callers relying on it for audit timestamps may silently receive local server time instead of database time.- The stateless session strategy requires explicit
Update()calls for every entity change (no automatic dirty-checking/flush-on-commit), unlike the stateful strategy whereUpdate()is a no-op relying on NHibernate's automatic dirty-checking. - Entity POCOs mapped throughout
Mappings/(SAIF.Data.Model.*) and the seam typesIUnitOfWork,IRepository<TEntity>,ISessionHelper,IDataEntityconsumed by this library are supplied by theSAIF.Framework.Corepackage dependency (namespaceSAIF.Data), not by the consuming application: this project'sRootNamespace(SAIF.Data.NHibernate) nests under Core'sSAIF.Datanamespace via C# enclosing-namespace lookup, so these types are in scope without ausing SAIF.Data;directive. This is an ordinary upstream package dependency, not a consumer-imposed design constraint. See Dependencies for theSAIF.Framework.Corepackage-dependency evidence. - This library performs no authorization/authentication enforcement and reads no application-level configuration keys (connection strings are supplied as plain method parameters by the caller); resolving those values from environment/app configuration is the consuming application's responsibility.
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.
Related documentation¶
- Index
- Overview
- Dependencies
- Operations and processes
- Business rules
- Database tables
- SQL catalog
- API reference
Sources¶
Factories/ConfigurationFactoryBase.csFactories/OracleConfigurationFactory.csFactories/SqlServerConfigurationFactory.csFactories/UnitOfWorkFactoryBase.csFactories/OracleUnitOfWorkFactory.csFactories/OracleStatelessUnitOfWorkFactory.csFactories/SqlServerUnitOfWorkFactory.csInterfaces/IConfigurationFactory.csInterfaces/IUnitOfWorkFactory.csInterfaces/ISessionStrategy.csSessions/StatefulSessionStrategy.csSessions/StatelessSessionStrategy.csSessionHelper/SessionHelperBase.csUnitOfWork/NHibernateUnitOfWork.csRepositories/RepositoryBase.csRepositories/Repository.csRepositories/Imaging/ImageRepositoryExtensions.csMappings/(42ClassMap<TEntity>files across 10 domain sub-folders)