【问题标题】:How does one gracefully merge object graphs after NHibernate StaleObjectStateException?NHibernate StaleObjectStateException 之后如何优雅地合并对象图?
【发布时间】:2011-12-24 19:28:19
【问题描述】:

我们正在尝试在抛出 StaleObjectStateException 后合并对象以保存合并副本。

这是我们的环境状况:

  • 列表项
  • 多用户系统
  • WPF 桌面应用程序、SQL Server 2008 数据库
  • NHibernate 3.1.0.4000,FluentNHibernate 1.2.0.712
  • 全球、长期运行的 NHibernate 会话 [暂时。我们知道每个演示者会话是推荐的模式,但目前在我们的项目计划中没有时间进行转换。]
  • 自上而下的保存和属性导航(也就是说我们在域图中保存了顶级对象(这里称为Parent))
  • .Cascade.AllDeleteOrphan() 在大多数情况下使用。
  • 用户独占域图中的某些对象,但共享父对象的所有权。
  • Children 对象的导航属性不存在。
  • 所有类都有数字 ID 和数字版本字段。

用例:

  • 用户 1 启动应用程序并打开父级。
  • 用户 2 启动应用程序并打开父级。
  • 用户 2 添加了一个孩子(此处为 C2)。
  • 用户 2 保存父级。
  • 用户 1 添加了一个孩子(此处为 C1)。
  • 用户 1 保存父级。
  • 用户 1 收到一个 StaleObjectStateException(这是正确的)

我们希望优雅地处理异常。 因为用户共享父级的所有权,所以用户 1 应该能够成功保存,并且将父级与他的新孩子和用户 2 的孩子一起保存。

当 SOSE 被抛出时,根据 Ayende (http://msdn.microsoft.com/en-us/magazine/ee819139.aspx):

您的会话及其加载的实体是 toast,因为使用 NHibernate,会引发异常 from a session 将该会话移至未定义状态。您不能再使用该会话 或任何加载的实体

C1 已经被现在不可用的会话分配了一个 ID 和版本号。 (我希望它没有。)

我们如何结合使用 ISession.Merge() 和 ISession.Refresh() 来获得一个新保存的同时具有 C1 和 C2 的 Parent ?

我们尝试了许多神秘的排列方式,但都没有完全奏效。 通常,“行已被另一个事务更新或删除(或未保存的值映射不正确”或 ODBC 级别的实际 ID 冲突。

我们目前的理论:

  1. 重置 C1 上的版本号(以防止“未保存的值映射不正确”)
  2. 获取新会话
  3. newSession.Refresh(C1);
  4. newParent = newSession.QueryOver[...]
  5. newParent.Add(C1);
  6. newSession.SaveOrUpdate(newParent)

但是,所有文档都表明 newSession.Merge 应该就足够了。

用作研究的其他帖子:
Fluent NHibernate Newbie: Row was updated or deleted by another transaction
Is there an alternative to ISession.Merge() that doesn't throw when using optimistic locking?
StaleObjectstateException row was updated or deleted by
How I can tell NHibernate to save only changed properties
Hibernate (JPA): how to handle StaleObjectStateException when several object has been modified and commited(java,但我认为是相关的)

【问题讨论】:

    标签: nhibernate fluent-nhibernate optimistic-locking staleobjectstate


    【解决方案1】:

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

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

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

    1. 在调用session.Flush() 之前,会话被序列化为MemoryStream。
    2. 如果session.Flush() 或transaction.Commit() 抛出可恢复的异常,则会释放原始会话并反序列化保存的会话。
    3. 调用屏幕然后获取会话在异常后恢复的信息,并再次调用第一次打开屏幕时调用的相同查询。由于所有修改后的实体仍在恢复的会话中,因此用户现在处于按下保存之前的状态。

    【讨论】:

    • 看起来我们需要修复我们的映射文件,然后使用 session.Replicate(obj, ReplicationMode.overwrite) ——除非一切都是可序列化的,否则内存流的东西不起作用,仍然是我们的版本数字再次导致 StaleObject
    • 注意:这仍然不是我最喜欢的答案,但 cremor 赢得了赏金。 :)
    猜你喜欢
    • 2022-11-25
    • 2010-11-24
    • 1970-01-01
    • 1970-01-01
    • 2019-06-21
    • 1970-01-01
    • 1970-01-01
    • 2013-09-18
    • 1970-01-01
    相关资源
    最近更新 更多