You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 16:07:04