9

マージされたコピーを保存するために StaleObjectStateException がスローされた後、オブジェクトを結合しようとしています。

当社の環境状況は次のとおりです。

  • リスト項目
  • マルチユーザーシステム
  • WPF デスクトップ アプリケーション、SQL Server 2008 データベース
  • NHibernate 3.1.0.4000、FluentNHibernate 1.2.0.712
  • グローバルで長期にわたる NHibernate セッション [今のところ. プレゼンターごとのセッションが推奨されるパターンであることは理解していますが、現時点ではプロジェクト スケジュールに変換する時間がありません。]
  • トップダウン保存とプロパティ ナビゲーション (つまり、トップレベル オブジェクト (ここでは親と呼ばれます) をドメイン グラフに保存します)
  • ほとんどの場合、.Cascade.AllDeleteOrphan() が使用されます。
  • ユーザーは、ドメイン グラフ内のいくつかのオブジェクトを排他的に所有しますが、親の所有権を共有します。
  • Children オブジェクトのナビゲーション プロパティは存在しません。
  • すべてのクラスには、数値の ID フィールドと数値のバージョン フィールドがあります。

使用事例:

  • ユーザー 1 がアプリケーションを起動し、親を開きます。
  • ユーザー 2 がアプリケーションを起動し、親を開きます。
  • ユーザー 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. newSession.Refresh(C1);
  4. newParent = newSession.QueryOver[...]
  5. newParent.Add(C1);
  6. newSession.SaveOrUpdate(newParent)

ただし、すべてのドキュメントは、newSession.Merge で十分であることを示唆しています

研究として使用されるその他の投稿:
流暢な NHibernate 初心者: 別のトランザクションによって行が更新または削除され

StaleObjectstateException 行は、
変更されたプロパティのみを保存するように NHibernate に指示する方法 によって更新または削除されました

4

1 に答える 1

2

ユーザーは親の所有権を共有しているため、ユーザー 1 は正常に保存でき、新しい子とユーザー 2 の子の両方で親を保存できます。

子コレクションの楽観的ロックを無効にしてみませんか? その後、誰でも子を追加でき、親のバージョンは上がりません。

それ以外の場合、現在のプロジェクトがセッションがスローする可能性のあるすべての回復可能な例外に対して使用するソリューションは次のとおりです (たとえば、DB への接続が失われた、外部キーが違反されたなど)。

  1. session.Flush()セッションを呼び出す前に、 MemoryStream.
  2. session.Flush()またはが回復可能な例外をスローした場合transaction.Commit()、元のセッションは破棄され、保存されたセッションは逆シリアル化されます。
  3. 次に、呼び出し元の画面は、例外の後にセッションが回復されたという情報を取得し、画面が最初に開かれたときに呼び出されたのと同じクエリを再度呼び出します。また、変更されたエンティティはすべて復元されたセッションに残っているため、ユーザーは保存を押す直前の状態になります。
于 2011-11-09T08:28:39.317 に答える