我们试图在抛出 StaleObjectStateException 后合并对象以保存合并的副本。
这是我们的环境状况:
- 项目清单
- 多用户系统
- WPF 桌面应用程序、SQL Server 2008 数据库
- NHibernate 3.1.0.4000,FluentNHibernate 1.2.0.712
- 全球、长期运行的 NHibernate 会话 [暂时。我们知道每个演示者会话是推荐的模式,但目前在我们的项目计划中没有时间进行转换。]
- 自上而下的保存和属性导航(也就是说我们在域图中保存了顶层对象(这里称为Parent))
- .Cascade.AllDeleteOrphan() 在大多数情况下使用。
- 用户独占域图中的某些对象,但共享父对象的所有权。
- Children 对象的导航属性不存在。
- 所有类都有数字 ID 和数字版本字段。
用例:
- 用户 1 启动应用程序并打开父级。
- 用户 2 启动应用程序并打开 Parent。
- 用户 2 添加了一个孩子(此处为 C2)。
- 用户 2 保存父级。
- 用户 1 添加了一个孩子(此处为 C1)。
- 用户 1 保存父级。
- 用户 1 收到一个 StaleObjectStateException (这是正确的)
我们希望优雅地处理异常。因为用户共享父级的所有权,所以用户 1 应该能够成功保存,并且将父级与他的新孩子和用户 2 的孩子一起保存。
根据 Ayende ( http://msdn.microsoft.com/en-us/magazine/ee819139.aspx ) 的说法,当 SOSE 被抛出时:
您的会话及其加载的实体是烤面包,因为使用 NHibernate,从会话中抛出的异常会将会话移动到未定义的状态。您不能再使用该会话或任何加载的实体
C1 已经被现在不可用的会话分配了一个 ID 和版本号。(我希望没有。)
我们如何结合使用 ISession.Merge() 和 ISession.Refresh() 来获得一个新保存的同时具有 C1 和 C2 的 Parent ?
我们尝试了许多神秘的排列方式,但都没有完全奏效。通常,“行已被另一个事务更新或删除(或未保存的值映射不正确”或 ODBC 级别的实际 ID 冲突。
目前我们的理论:
- 重置 C1 上的版本号(以防止“未保存的值映射不正确”)
- 获取新会话
- 新会话。刷新(C1);
- newParent = newSession.QueryOver[...]
- newParent.Add(C1);
- newSession.SaveOrUpdate(newParent)
但是,所有文档都表明 newSession.Merge应该就足够了。
用作研究的其他帖子:
Fluent NHibernate Newbie:行已被另一个事务更新或删除
是否有替代 ISession.Merge() 的方法在使用乐观锁定时不会抛出?
StaleObjectstateException 行由
How I can tell NHibernate to save only changed properties
Hibernate (JPA): how to handle StaleObjectStateException when multiple object has been modified and commited 更新或删除(java,但相关,我认为)