基于NHibernate提取聚合状态实现LAN多实例共享的技术问询
我正在维护一个遵循DDD原则的WPF应用,该应用高度注重状态封装(即尽可能将字段设为私有)。聚合的状态通过NHibernate持久化到SQLite数据库,映射由FluentNHibernate声明,无其他应用访问该数据库。
我面临一项新需求:特定聚合需在同一LAN内不同计算机上运行的同一应用多实例间共享访问。
我了解多种可行方案(对象数据库、DTO序列化、文件共享等),但这些都需要大量新工作来搭建。
在实现新的状态提取方式前,我希望深入了解利用NHibernate内置功能的可能性:
NHibernate在持久化聚合时,可借助映射、自定义用户类型及反射提取原始值(string、int、decimal等),注入SQL查询以完成持久化。
是否存在拦截这些原始值的方式,以便我将其用于其他操作?我的核心想法是:不仅用NHibernate本地持久化状态,还借此获取状态的“序列化”版本(无需额外配置或映射),通过网络传输后,在另一端借助NHibernate反向操作实现对象 hydration。
目前我在NHibernate中发现EntityEntry.LoadedState可能包含所需信息,它是否适用于我的需求?具体应如何:
- 获取目标聚合的该属性?
- 通过该属性hydrate聚合对象?
根据David Osborne的回答,我已通过领域事件处理器确定了操作时机:
public class SaveCommittedOrderClosed : IDomainEventHandler<CommittedOrderClosed> { public Result<Unit, Error> Handle(IUnitOfWork uow, CommittedOrderClosed @event) { //能否在此处通过NHibernate提取@event.Order的状态? } }
我欠缺的是具体实现方法。
具体实现方法
1. 在事件处理器中获取EntityEntry.LoadedState
首先需要从工作单元中获取NHibernate会话,再提取目标聚合的EntityEntry及对应状态:
public Result<Unit, Error> Handle(IUnitOfWork uow, CommittedOrderClosed @event) { // 将IUnitOfWork转换为NHibernate的ISession(需确保你的UoW封装了ISession) var session = uow as ISession; if (session == null) return Result.Fail<Unit, Error>(new Error("无法获取NHibernate会话")); // 获取聚合对应的EntityEntry var entityEntry = session.PersistenceContext.GetEntry(@event.Order); if (entityEntry == null) return Result.Fail<Unit, Error>(new Error("未找到目标聚合的EntityEntry")); // 获取原始状态数组及对应属性名 object[] loadedState = entityEntry.LoadedState; string[] propertyNames = entityEntry.Persister.PropertyNames; // 转为字典便于后续处理或序列化传输 var stateDictionary = propertyNames.Zip(loadedState, (name, value) => new {name, value}) .ToDictionary(kv => kv.name, kv => kv.value); // 此处可将stateDictionary序列化为JSON/二进制,通过LAN传输到其他实例 // ... return Result.Ok<Unit, Error>(Unit.Value); }
LoadedState存储了聚合当前持久化的原始值,顺序与映射中定义的属性完全一致,配合PropertyNames可明确每个值对应的属性,且无需破坏聚合的私有封装。
2. 在接收端通过状态反向hydrate聚合对象
在另一实例的NHibernate环境中,利用IEntityPersister完成对象重建:
public T HydrateAggregate<T>(ISession session, object[] loadedState) where T : class { var sessionImpl = session.GetSessionImplementation(); // 获取对应聚合的实体持久化器 var persister = sessionImpl.Factory.GetEntityPersister(typeof(T).FullName); // 创建聚合实例(NHibernate会自动处理私有构造函数调用) var aggregate = persister.Instantiate(loadedState, sessionImpl); // 将状态值注入聚合的私有字段/属性 persister.SetPropertyValues(aggregate, loadedState, sessionImpl); // 可选:将聚合加入会话上下文,避免重复加载或状态不一致 session.PersistenceContext.AddEntity(aggregate, EntityStatus.Loaded, loadedState, null, persister.GetIdentifier(aggregate, sessionImpl), null, LockMode.None, true); return aggregate; }
注意事项
- 两端应用的NHibernate映射必须完全一致,包括属性顺序、类型、自定义用户类型,否则会导致状态注入失败
- 若聚合包含关联对象,
LoadedState仅存储关联实体的ID,需确保接收端数据库中已存在对应关联实体,或额外处理关联实体的同步 - 该方式依赖NHibernate的内部API,升级NHibernate版本时需注意兼容性
内容的提问来源于stack exchange,提问作者Spotted

