NHibernate in ConnectSoft Microservice Template¶
Purpose & Overview¶
NHibernate is an open-source Object-Relational Mapping (ORM) framework for .NET that provides a comprehensive solution for mapping .NET domain models to relational databases. In the ConnectSoft Microservice Template, NHibernate serves as one of the primary persistence technologies, alongside MongoDB, enabling type-safe database operations with SQL Server, PostgreSQL, MySQL, and other relational databases.
NHibernate provides:
- ORM Mapping: Automatic mapping between .NET objects and database tables
- Fluent NHibernate: Code-based mapping configuration (alternative to XML)
- Session Management: Automatic session lifecycle and transaction management
- Query Languages: HQL (Hibernate Query Language), Criteria API, LINQ provider
- Second-Level Cache: Distributed caching support with Redis
- Connection Pooling: Built-in connection pool management
- Lazy Loading: Efficient data loading with lazy associations
- Transaction Support: ACID-compliant transaction management
- Schema Generation: Optional automatic schema generation (though FluentMigrator is preferred)
NHibernate Philosophy
NHibernate aims to reduce the impedance mismatch between object-oriented programming and relational databases. It provides a powerful yet flexible mapping framework that allows developers to work with objects while NHibernate handles the SQL translation and data persistence.
Architecture Overview¶
NHibernate in ConnectSoft Architecture¶
Application Layer
├── Processors (Commands/Writes)
└── Retrievers (Queries/Reads)
↓ (Uses Repository Interface)
Infrastructure Layer
├── PersistenceModel.NHibernate
│ ├── Repositories (NHibernate Implementation)
│ ├── Mappings (Fluent NHibernate)
│ └── Specifications (Query Specifications)
├── NHibernate Session Factory
├── Session Management (Per HTTP Request)
└── Unit of Work (Transaction Management)
↓
Database Layer
└── SQL Server / PostgreSQL / MySQL
Key Integration Points¶
| Layer | Component | Responsibility |
|---|---|---|
| PersistenceModel | IMicroserviceAggregateRootsRepository |
Repository interface (abstraction) |
| PersistenceModel.NHibernate | MicroserviceAggregateRootsRepository |
NHibernate repository implementation |
| PersistenceModel.NHibernate.Mappings | MicroserviceAggregateRootEntityMap |
Fluent NHibernate entity mappings |
| ApplicationModel | NHibernateExtensions |
NHibernate configuration and registration |
| Framework | ConnectSoft.Extensions.PersistenceModel.NHibernate |
Base repository and session management |
Configuration¶
Service Registration¶
NHibernate is registered via extension methods in the application startup:
// MicroserviceRegistrationExtensions.cs
public static IServiceCollection ConfigureMicroserviceServices(
this IServiceCollection services,
IConfiguration configuration,
IWebHostEnvironment environment)
{
// ... other service registrations ...
#if Migrations
services.AddMicroserviceFluentMigrator(configuration, typeof(MicroserviceMigration).Assembly);
#endif
#if UseNHibernate
services.AddNHibernatePersistenceModel(configuration);
#endif
#if (UseNHibernate || UseMongoDb)
services.AddPersistenceModel();
#endif
return services;
}
NHibernate Extension Method¶
The AddNHibernatePersistenceModel() method configures all NHibernate services:
// NHibernateExtensions.cs
internal static IServiceCollection AddNHibernatePersistenceModel(
this IServiceCollection services,
IConfiguration configuration)
{
ArgumentNullException.ThrowIfNull(services);
string hibernateConfig = OptionsExtensions.PersistenceModelOptions.NHibernate.NHibernateConfigFile;
services.AddNHibernateFromConfiguration(
filePath: Path.Combine(AppDomain.CurrentDomain.BaseDirectory, hibernateConfig),
nhibernateMappingsAssembly: typeof(MicroserviceAggregateRootEntityMap).Assembly,
systemConfiguration: configuration,
dependencyInjectionKey: MicroserviceConstants.NHibernateDIKey);
if (!string.IsNullOrEmpty(MicroserviceConstants.NHibernateDIKey))
{
// Keyed services (for multi-database scenarios)
services.AddKeyedScoped<IMicroserviceAggregateRootsRepository, MicroserviceAggregateRootsKeyedRepository>(
MicroserviceConstants.NHibernateDIKey);
services.AddKeyedScoped<IMicroserviceAggregateRootsSpecification, MicroserviceAggregateRootsQueryableKeyedSpecification>(
MicroserviceConstants.NHibernateDIKey);
}
else
{
// Standard services (single database scenario)
services.AddScoped<IMicroserviceAggregateRootsRepository, MicroserviceAggregateRootsRepository>();
services.AddScoped<IMicroserviceAggregateRootsSpecification, MicroserviceAggregateRootsQueryableSpecification>();
}
return services;
}
NHibernate Configuration File¶
NHibernate is configured via hibernate.cfg.xml:
<!-- hibernate.cfg.xml -->
<session-factory xmlns="urn:nhibernate-configuration-2.2"
name="ConnectSoft.MicroserviceTemplateSqlServer">
<!-- Database Dialect -->
<property name="dialect">NHibernate.Dialect.MsSql2012Dialect</property>
<!-- Database Driver -->
<property name="connection.driver_class">
NHibernate.Driver.MicrosoftDataSqlClientDriver
</property>
<!-- Connection String Key (from appsettings.json) -->
<property name="connection.connection_string_name">
ConnectSoft.MicroserviceTemplateSqlServer
</property>
<!-- Performance Settings -->
<property name="adonet.batch_size">100</property>
<property name="command_timeout">60</property>
<property name="hbm2ddl.keywords">auto-quote</property>
<!-- SQL Logging (development) -->
<property name="show_sql">true</property>
<property name="format_sql">true</property>
<!-- Second-Level Cache (Redis) -->
<!--#if (UseRedisAsNHibernateSecondLevelCache) -->
<property name="cache.provider_class">
NHibernate.Caches.StackExchangeRedis.RedisCacheProvider,
NHibernate.Caches.StackExchangeRedis
</property>
<property name="cache.default_expiration">300</property>
<property name="cache.use_second_level_cache">true</property>
<property name="cache.use_query_cache">true</property>
<property name="cache.region_prefix">ConnectSoft.MicroserviceTemplate.Application</property>
<property name="cache.configuration">localhost:6379,ConnectRetry=3,KeepAlive=180</property>
<property name="cache.serializer">
NHibernate.Caches.Util.JsonSerializer.JsonCacheSerializer,
NHibernate.Caches.Util.JsonSerializer
</property>
<!--#endif -->
</session-factory>
Configuration Options¶
| Option | Description | Default |
|---|---|---|
dialect |
Database-specific SQL dialect | MsSql2012Dialect |
connection.driver_class |
Database driver class | MicrosoftDataSqlClientDriver |
connection.connection_string_name |
Connection string key from appsettings.json | Required |
adonet.batch_size |
Batch size for bulk operations | 100 |
command_timeout |
Command timeout (seconds) | 60 |
show_sql |
Log SQL statements (development) | true |
format_sql |
Format SQL in logs | true |
cache.use_second_level_cache |
Enable second-level cache | false |
cache.use_query_cache |
Enable query cache | false |
appsettings.json Configuration¶
{
"ConnectionStrings": {
"ConnectSoft.MicroserviceTemplateSqlServer": "Server=localhost;Database=MicroserviceDB;Integrated Security=true;"
},
"PersistenceModel": {
"NHibernate": {
"NHibernateConfigFile": "hibernate.cfg.xml",
"NHibernateConnectionStringKey": "ConnectSoft.MicroserviceTemplateSqlServer"
}
}
}
Supported Database Dialects¶
| Database | Dialect Class |
|---|---|
| SQL Server 2012+ | NHibernate.Dialect.MsSql2012Dialect |
| SQL Server 2008 | NHibernate.Dialect.MsSql2008Dialect |
| PostgreSQL | NHibernate.Dialect.PostgreSQL83Dialect |
| MySQL | NHibernate.Dialect.MySQLDialect |
| Oracle | NHibernate.Dialect.Oracle12cDialect |
| SQLite | NHibernate.Dialect.SQLiteDialect |
Entity Mappings¶
Fluent NHibernate Mapping¶
Entity mappings are defined using Fluent NHibernate's ClassMap<T>:
// MicroserviceAggregateRootEntityMap.cs
namespace ConnectSoft.MicroserviceTemplate.PersistenceModel.NHibernate.Mappings
{
using System;
using ConnectSoft.MicroserviceTemplate.EntityModel.PocoEntities;
using FluentNHibernate.Mapping;
/// <summary>
/// Defines a NHibernate mapping for a <see cref="MicroserviceAggregateRootEntity"/> entity.
/// </summary>
public class MicroserviceAggregateRootEntityMap : ClassMap<MicroserviceAggregateRootEntity>
{
public MicroserviceAggregateRootEntityMap()
{
// Schema and table names (with quoted identifiers)
this.Schema("`" + PersistenceModelConstants.Schema + "`");
this.Table("`MicroserviceAggregateRoots`");
// Primary key mapping
this.Id(m => m.ObjectId)
.Column(nameof(MicroserviceAggregateRootEntity.ObjectId))
.GeneratedBy.Assigned()
.UnsavedValue(Guid.Empty);
// Additional property mappings would go here
// this.Map(m => m.SomeValue).Column("SomeValue").Not.Nullable();
// this.Map(m => m.CreatedOn).Column("CreatedOn");
}
}
}
Mapping Examples¶
Simple Property Mapping¶
Collection Mapping (One-to-Many)¶
Reference Mapping (Many-to-One)¶
Component Mapping (Value Object)¶
this.Component(m => m.Address, address =>
{
address.Map(a => a.Street);
address.Map(a => a.City);
address.Map(a => a.PostalCode);
});
Cache Mapping¶
Mapping Auto-Discovery¶
NHibernate automatically discovers mappings in the specified assembly:
services.AddNHibernateFromConfiguration(
nhibernateMappingsAssembly: typeof(MicroserviceAggregateRootEntityMap).Assembly,
// ... other parameters ...
);
All classes inheriting from ClassMap<T> in this assembly are automatically registered.
Repository Implementation¶
Repository Base Class¶
Repositories inherit from GenericRepository<TEntity, TIdentity> provided by the framework:
// MicroserviceAggregateRootsRepository.cs
namespace ConnectSoft.MicroserviceTemplate.PersistenceModel.NHibernate.Repositories
{
using System;
using ConnectSoft.Extensions.PersistenceModel;
using ConnectSoft.Extensions.PersistenceModel.Repositories;
using ConnectSoft.Extensions.PersistenceModel.Specifications;
using ConnectSoft.MicroserviceTemplate.EntityModel;
using ConnectSoft.MicroserviceTemplate.PersistenceModel.Repositories;
/// <summary>
/// Generic repository implementation that provides unified access
/// to the MicroserviceAggregateRoots entities stored in underlying data storage.
/// </summary>
public class MicroserviceAggregateRootsRepository(
IUnitOfWork unitOfWork,
ISpecificationLocator specificationLocator)
: GenericRepository<IMicroserviceAggregateRoot, Guid>(
unitOfWork,
specificationLocator),
IMicroserviceAggregateRootsRepository
{
// Inherits all CRUD operations from GenericRepository:
// - Insert, InsertAsync
// - Update, UpdateAsync
// - Delete, DeleteAsync
// - GetById, GetByIdAsync
// - GetAll, GetAllAsync
// - Query, QueryAsync
// - Specify<T> (Specification Pattern)
}
}
Keyed Repository (Multi-Database)¶
For scenarios with multiple databases, repositories can use keyed services:
// MicroserviceAggregateRootsKeyedRepository.cs
public class MicroserviceAggregateRootsKeyedRepository(
[FromKeyedServices(MicroserviceConstants.NHibernateDIKey)] IUnitOfWork unitOfWork,
[FromKeyedServices(MicroserviceConstants.NHibernateDIKey)] ISpecificationLocator specificationLocator)
: GenericRepository<IMicroserviceAggregateRoot, Guid>(unitOfWork, specificationLocator),
IMicroserviceAggregateRootsRepository
{
// Same functionality as non-keyed repository
// But uses keyed Unit of Work and Specification Locator
}
Repository Operations¶
The GenericRepository base class provides:
CRUD Operations¶
// Insert
await repository.InsertAsync(entity, cancellationToken);
// Update
await repository.UpdateAsync(entity, cancellationToken);
// Delete
await repository.DeleteAsync(entity, cancellationToken);
await repository.DeleteAsync(id, cancellationToken);
// Get by ID
var entity = await repository.GetByIdAsync(id, cancellationToken);
// Get all
var allEntities = await repository.GetAllAsync(cancellationToken);
Query Operations¶
// Expression-based query
var results = await repository.QueryAsync(
x => x.Status == "Active" && x.CreatedOn >= startDate,
cancellationToken);
// Specification-based query
var specification = repository.Specify<IActiveAggregatesSpecification>()
.Where(x => x.CreatedOn >= startDate);
var results = await specification.ToListAsync(cancellationToken);
Session Management¶
Session Lifecycle¶
NHibernate sessions are managed per HTTP request (scoped):
- Session Creation: Session created at request start
- Session Usage: Repositories use session for operations
- Transaction Management: Unit of Work manages transactions
- Session Disposal: Session disposed at request end
Session Factory¶
The session factory is registered as a singleton:
// Provided by ConnectSoft.Extensions.PersistenceModel.NHibernate
services.AddNHibernateFromConfiguration(
// Creates ISessionFactory (singleton)
// Creates ISession (scoped per HTTP request)
);
Session Scoping¶
- ISessionFactory: Singleton (created once, reused)
- ISession: Scoped (one per HTTP request)
- ITransaction: Managed by Unit of Work pattern
Unit of Work Pattern¶
Transaction Management¶
NHibernate integrates with the Unit of Work pattern for transaction management:
// Unit of Work is registered as scoped service
services.AddScoped<IUnitOfWork, NHibernateUnitOfWork>();
Using Unit of Work¶
public class DefaultMicroserviceAggregateRootsProcessor : IMicroserviceAggregateRootsProcessor
{
private readonly IUnitOfWork unitOfWork;
private readonly IMicroserviceAggregateRootsRepository repository;
public async Task<IMicroserviceAggregateRoot> CreateMicroserviceAggregateRoot(
CreateMicroserviceAggregateRootInput input,
CancellationToken token = default)
{
// Begin transaction
using var transaction = await this.unitOfWork.BeginTransactionAsync(token);
try
{
var aggregate = new MicroserviceAggregateRootEntity
{
ObjectId = input.ObjectId,
SomeValue = input.SomeValue
};
// Persist (uses NHibernate session)
await this.repository.InsertAsync(aggregate, token);
// Commit transaction
await transaction.CommitAsync(token);
return aggregate;
}
catch
{
// Transaction rolls back automatically on disposal if not committed
throw;
}
}
}
ExecuteTransactional Helper¶
Some implementations provide a helper method:
unitOfWork.ExecuteTransactional(() =>
{
repository.Insert(newEntity);
repository.Update(existingEntity);
// All operations are in same transaction
});
Querying¶
HQL (Hibernate Query Language)¶
NHibernate supports HQL for database-agnostic queries:
// HQL Query
var results = session.CreateQuery(
"from MicroserviceAggregateRootEntity where Status = :status")
.SetParameter("status", "Active")
.List<MicroserviceAggregateRootEntity>();
LINQ Provider¶
NHibernate provides a LINQ provider:
var results = session.Query<MicroserviceAggregateRootEntity>()
.Where(x => x.Status == "Active" && x.CreatedOn >= startDate)
.OrderBy(x => x.CreatedOn)
.ToList();
Criteria API¶
For dynamic queries:
var criteria = session.CreateCriteria<MicroserviceAggregateRootEntity>()
.Add(Restrictions.Eq("Status", "Active"))
.Add(Restrictions.Ge("CreatedOn", startDate))
.AddOrder(Order.Desc("CreatedOn"));
var results = criteria.List<MicroserviceAggregateRootEntity>();
Specification Pattern¶
The template uses the Specification Pattern for complex queries (see Specification Pattern):
var specification = repository.Specify<IMicroserviceAggregateRootsSpecification>()
.Where(x => x.Status == "Active")
.And(x => x.CreatedOn >= startDate);
var results = await specification.ToListAsync(cancellationToken);
Second-Level Cache¶
Redis Cache Configuration¶
NHibernate supports second-level caching with Redis:
<!-- hibernate.cfg.xml -->
<property name="cache.provider_class">
NHibernate.Caches.StackExchangeRedis.RedisCacheProvider,
NHibernate.Caches.StackExchangeRedis
</property>
<property name="cache.use_second_level_cache">true</property>
<property name="cache.use_query_cache">true</property>
<property name="cache.region_prefix">ConnectSoft.MicroserviceTemplate.Application</property>
<property name="cache.configuration">localhost:6379,ConnectRetry=3,KeepAlive=180</property>
<property name="cache.default_expiration">300</property>
Entity Cache Configuration¶
Enable caching on entities via mapping:
// In ClassMap<T>
public MicroserviceAggregateRootEntityMap()
{
// Enable second-level cache
this.Cache
.ReadWrite() // Read-write cache strategy
.Region("aggregate-root"); // Cache region
}
Query Cache¶
Enable caching on queries:
var results = session.CreateQuery(
"from MicroserviceAggregateRootEntity where Status = :status")
.SetParameter("status", "Active")
.SetCacheable(true) // Enable query cache
.SetCacheRegion("active-aggregates") // Cache region
.List<MicroserviceAggregateRootEntity>();
Cache Strategies¶
| Strategy | Description | Use Case |
|---|---|---|
| Read-Only | Cache.ReadOnly() |
Immutable entities |
| Read-Write | Cache.ReadWrite() |
Read-heavy, occasionally updated |
| Nonstrict-Read-Write | Cache.NonstrictReadWrite() |
Rarely updated |
| Transactional | Cache.Transactional() |
High consistency required |
See Caching for detailed information.
Database Migrations¶
FluentMigrator Integration¶
The template uses FluentMigrator for database schema management (not NHibernate's schema generation):
// MicroserviceRegistrationExtensions.cs
#if Migrations
services.AddMicroserviceFluentMigrator(configuration, typeof(MicroserviceMigration).Assembly);
#endif
Migrations run at application startup:
Migration Example¶
[Migration(20240101000000)]
public class CreateMicroserviceAggregateRootsTable : Migration
{
public override void Up()
{
Create.Table("MicroserviceAggregateRoots")
.InSchema("dbo")
.WithColumn("ObjectId").AsGuid().PrimaryKey()
.WithColumn("SomeValue").AsString(255).Nullable();
}
public override void Down()
{
Delete.Table("MicroserviceAggregateRoots").InSchema("dbo");
}
}
Migration Strategy
While NHibernate supports automatic schema generation (hbm2ddl.auto), the template uses FluentMigrator for production-ready, version-controlled database migrations. This ensures schema changes are tracked, reversible, and can be applied to multiple environments consistently.
Logging¶
NHibernate Logging Integration¶
NHibernate integrates with Microsoft.Extensions.Logging:
// MicroserviceRegistrationExtensions.cs
#if UseNHibernate
loggerFactory.UseNHibernateLogging();
#endif
Logging Configuration¶
NHibernate logs can be filtered in appsettings.json:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"NHibernate": "Debug",
"NHibernate.SQL": "Debug",
"NHibernate.Impl": "Warning"
}
}
}
SQL Logging¶
Enable SQL statement logging:
<!-- hibernate.cfg.xml -->
<property name="show_sql">true</property>
<property name="format_sql">true</property>
Health Checks¶
NHibernate Health Check¶
NHibernate persistence is monitored via health checks:
// HealthChecksExtensions.cs
#if UseNHibernate
builder.AndAndConfigureNHibernatePersistenceHealthCheck(configuration);
#endif
The health check verifies: - Database connectivity - Session factory availability - Basic query execution
See Health Checks for detailed information.
OpenTelemetry Integration¶
SQL Instrumentation¶
NHibernate operations are automatically traced via SQL Client instrumentation:
// OpenTelemetryExtensions.cs
#if UseNHibernate
tracingBuilder.AddSqlClientInstrumentation(options =>
{
options.RecordException = true;
options.SetDbStatementForText = true;
options.EnableConnectionLevelAttributes = true;
});
#endif
Traced operations include: - SQL query execution - Connection management - Transaction operations - Batch operations
Metrics¶
NHibernate metrics are available via OpenTelemetry: - Query execution time - Connection pool usage - Transaction duration - Cache hit/miss ratios
See Logging for detailed observability information.
Testing¶
Unit Testing Mappings¶
Mappings can be validated using Fluent NHibernate's PersistenceSpecification:
// MicroserviceNHibernateClassMappingsUnitTests.cs
[TestMethod]
public void CanCorrectlyMapMicroserviceAggregateRootEntityUsingFluentNHibernate()
{
// Arrange
var serviceProvider = CreateServiceProvider();
var session = serviceProvider.GetRequiredService<ISessionSource>();
// Act and Assert
new PersistenceSpecification<MicroserviceAggregateRootEntity>(session)
.CheckProperty(c => c.ObjectId, Guid.NewGuid())
.VerifyTheMappings();
}
Integration Testing¶
[TestMethod]
public async Task Repository_Should_Save_And_Retrieve_Entity()
{
// Arrange
var repository = serviceProvider.GetRequiredService<IMicroserviceAggregateRootsRepository>();
var unitOfWork = serviceProvider.GetRequiredService<IUnitOfWork>();
var entity = new MicroserviceAggregateRootEntity { ObjectId = Guid.NewGuid() };
// Act
using var transaction = await unitOfWork.BeginTransactionAsync();
await repository.InsertAsync(entity);
await transaction.CommitAsync();
// Assert
var retrieved = await repository.GetByIdAsync(entity.ObjectId);
Assert.IsNotNull(retrieved);
Assert.AreEqual(entity.ObjectId, retrieved.ObjectId);
}
Best Practices¶
Do's¶
- Use Fluent NHibernate for Mappings
- Code-based mappings are more maintainable than XML
- Better IntelliSense and compile-time checking
-
Easier refactoring
-
Always Use Transactions
-
Use Async Methods
-
Enable Second-Level Cache for Read-Heavy Entities
-
Use Specifications for Complex Queries
- Encapsulates query logic
- Reusable and testable
-
Maintains Clean Architecture boundaries
-
Quote Identifiers for Schema/Table Names
Don'ts¶
-
Don't Use Schema Generation in Production
-
Don't Mix Session and Repository Abstractions
-
Don't Forget to Handle Null Returns
-
Don't Use N+1 Queries
// ❌ BAD - N+1 query problem var aggregates = await repository.GetAllAsync(); foreach (var agg in aggregates) { var items = await LoadItemsForAggregate(agg.Id); // N queries! } // ✅ GOOD - Use eager loading or batch loading var aggregates = session.Query<MicroserviceAggregateRootEntity>() .FetchMany(a => a.Items) .ToList(); -
Don't Expose ISession or ISessionFactory
- Always use repository interfaces
- Maintains Clean Architecture boundaries
- Enables easy swapping of persistence technologies
Troubleshooting¶
Issue: Session is Closed¶
Symptom: ObjectDisposedException or "Session is closed" errors.
Cause: Session disposed before operation completes, or accessing lazy-loaded properties outside session scope.
Solution:
// ✅ GOOD - Eager load associations within session
var entity = session.Query<Entity>()
.Fetch(e => e.ChildCollection)
.FirstOrDefault();
// ✅ GOOD - Map associations to DTOs before session closes
var dto = mapper.Map<EntityDto>(entity);
// Session can now close, DTO is detached
Issue: LazyInitializationException¶
Symptom: "Initializing proxy" exceptions when accessing lazy-loaded properties.
Cause: Accessing lazy-loaded properties outside active session.
Solution:
- Use eager loading: .Fetch() or .FetchMany()
- Map to DTOs before session closes
- Use Session.Flush() if needed
Issue: Query Performance Issues¶
Symptom: Slow queries, high database load.
Causes and Solutions:
-
N+1 Queries: Use eager loading or batch fetching
-
Missing Indexes: Add database indexes for frequently queried columns
-
Inefficient Queries: Use
.Select()to limit columns: -
Large Result Sets: Use pagination:
Issue: Transaction Not Committing¶
Cause: Missing Commit() call or exception before commit.
Solution:
using var transaction = await unitOfWork.BeginTransactionAsync();
try
{
await repository.InsertAsync(entity);
await transaction.CommitAsync(); // ✅ Must call Commit()
}
catch
{
// Transaction rolls back on disposal if not committed
throw;
}
Issue: Mapping Not Found¶
Symptom: "No persister for" errors.
Cause: Mapping class not discovered or assembly not registered.
Solution: Verify mapping class inherits from ClassMap<T> and is in the registered assembly:
services.AddNHibernateFromConfiguration(
nhibernateMappingsAssembly: typeof(MicroserviceAggregateRootEntityMap).Assembly,
// ...
);
Summary¶
NHibernate in the ConnectSoft Microservice Template provides:
- ✅ ORM Mapping: Fluent NHibernate for type-safe entity mappings
- ✅ Repository Pattern: Clean abstraction over data access
- ✅ Session Management: Automatic per-request session lifecycle
- ✅ Transaction Support: Unit of Work pattern for ACID compliance
- ✅ Query Flexibility: HQL, LINQ, Criteria API, and Specifications
- ✅ Caching: Second-level cache with Redis support
- ✅ Migration Support: FluentMigrator integration for schema management
- ✅ Observability: OpenTelemetry integration for tracing and metrics
- ✅ Health Monitoring: Health checks for persistence layer
- ✅ Testing Support: Mapping validation and integration test utilities
By following these patterns, microservices achieve:
- Clean Architecture — Persistence details isolated from domain and application layers
- Type Safety — Compile-time checking of queries and mappings
- Performance — Efficient querying, caching, and batch operations
- Maintainability — Code-based mappings, specifications, and repository abstractions
- Scalability — Connection pooling, second-level cache, and optimized queries
- Testability — Mockable repositories and in-memory test scenarios
The NHibernate integration ensures that ConnectSoft microservices can efficiently persist and retrieve domain entities while maintaining clean architectural boundaries and excellent observability.