9

我们试图在抛出 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 冲突。

目前我们的理论:

  1. 重置 C1 上的版本号(以防止“未保存的值映射不正确”)
  2. 获取新会话
  3. 新会话。刷新(C1);
  4. newParent = newSession.QueryOver[...]
  5. newParent.Add(C1);
  6. 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,但相关,我认为)

4

1 回答 1

2

因为用户共享父级的所有权,所以用户 1 应该能够成功保存,并且将父级与他的新孩子和用户 2 的孩子一起保存。

为什么不禁用子集合的乐观锁定?然后任何人都可以添加孩子,它不会增加父母的版本。

否则,这是我当前项目用于会话可能引发的所有可恢复异常的解决方案(例如,与 DB 的连接丢失、外键被违反……):

  1. 在调用session.Flush()会话之前将其序列化为MemoryStream.
  2. 如果session.Flush()transaction.Commit()抛出一个可恢复的异常,原始会话被释放,保存的会话被反序列化。
  3. 调用屏幕然后获取会话在异常后恢复的信息,并再次调用第一次打开屏幕时调用的相同查询。而且因为所有修改过的实体仍在恢复的会话中,所以用户现在处于按下保存之前的状态。
于 2011-11-09T08:28:39.317 回答