9

我們試圖在拋出StaleObjectStateException之後合併對象以保存合併副本。如何在NHibernate StaleObjectStateException之後優雅地合併對象圖?

下面是我們的環境狀況:

  • 列表項
  • 多用戶系統
  • WPF Desktop應用程序,SQL Server 2008數據庫
  • NHibernate的3.1.0.4000,FluentNHibernate 1.2.0.712
  • 全球性的,長期運行的NHibernate會話[暫時。我們瞭解會議每位演示者是推薦模式,但目前我們的項目時間表沒有時間進行轉換。]
  • 自上而下保存和屬性導航(也就是說我們保存頂級對象(在我們的域圖中)
  • .Cascade.AllDeleteOrphan()在大多數情況下使用。
  • 用戶只擁有域圖中的一些對象,但共享父對象的所有權。
  • Children對象的導航屬性不存在。
  • 所有類都有數字ID和數字版本字段。

使用案例:

  • 用戶1個啓動應用程序並打開父。
  • 用戶2啓動應用程序並打開Parent。
  • 用戶2添加一個孩子(此處爲C2)。
  • 用戶2保存父。
  • 用戶1添加一個孩子(此處爲C1)。
  • 用戶1保存父。
  • 用戶1接收StaleObjectStateException(這是正確的)

我們要優雅地處理例外。 因爲用戶共享父項的所有權,所以用戶1應該能夠成功保存,並將父項與他的新孩子以及用戶2的孩子一起保存。

當SOSE被拋出,根據Ayende(http://msdn.microsoft.com/en-us/magazine/ee819139.aspx):

您的會話和其加載實體敬酒,因爲與NHibernate,從會話拋出 一個異常會話移動到一個未定義的狀態。你可以不再使用該會話 或任何裝載的實體

C1已經被現在而不是無用的會話分配一個ID和版本號。 (我希望它沒有。)

我們如何結合使用ISession.Merge()和ISession.Refresh()來獲得一個新保存的同時擁有C1和C2的父?

我們嘗試了一些神祕的組合,其中沒有一個完全奏效。 通常情況下,無論是「行被其它事務更新或刪除(或者未保存值的映射是不正確的」,或在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的,但相關的,我認爲)

回答

2

因爲用戶共享父,用戶的所有權1應該能夠成功保存,並且保存Parent與他的新孩子和User 2的孩子。

爲什麼不只是禁用子集合上的樂觀鎖定?然後任何人都可以添加孩子,並且不會增加父母的版本。

否則,這裏是所有可恢復的異常會話可能拋出我當前的項目使用的解決方案(例如,以DB連接丟失,外鍵違反,...):

  1. 調用session.Flush()的會話之前序列化爲MemoryStream
  2. 如果session.Flush()transaction.Commit()引發可恢復的異常,則會丟棄原始會話並將保存的會話反序列化。
  3. 然後調用屏幕獲取會話在異常後被恢復的信息,並再次調用第一次打開屏幕時調用的相同查詢。並且由於所有修改的實體仍處於恢復的會話中,所以用戶現在具有剛按下保存之前的狀態。
+0

看起來我們需要同時修復我們的映射文件,然後使用session.Replicate(obj,ReplicationMode.overwrite) - 除非所有內容都是可序列化的,並且仍然是我們的版本號導致StaleObject再請 – BufferUnderrunOK

+0

注意:這仍然不是我最喜歡的答案,但是cremor已經贏得了賞金。 :) – BufferUnderrunOK