ご存知のように、NHibernate セッションはスレッド セーフではありません。しかし、コード パスがいくつかの実行時間の長いスレッドに分割されており、すべて初期スレッドに読み込まれたオブジェクトを使用しています。
using (var session = factory.OpenSession())
{
var parent = session.Get<T>(parentId);
DoSthWithParent(session, parent);
foreach (var child in parent.children)
{
parallelThreadMethodLongRunning.BeginInvoke(session, child);
//[Thread #1] DoSthWithChild(child #1) -> SaveOrUpdate(child #1) + Flush()
//[Thread #2] DoSthWithChild(child #2) -> SaveOrUpdate(child #2) + Flush()
//[Thread #3] DoSthWithChild(child #3) -> SaveOrUpdate(child #3) + Flush()
// -> etc... changes to be persisted immediately, not all at the end.
EndInvoke();
}
DoFinalChangesOnParentAndChildren(parent);
session.Flush();
}
}
1 つの方法は各スレッドのセッションですが、その場合は親オブジェクトをそれぞれに再ロードする必要があります。さらに、最後のメソッドは子に対しても変更を行っており、その間に別のセッションが変更した場合、または削除/再ロードする必要があった場合は、StaleObjectException で実行されます。
したがって、すべてのスレッドが同じセッションを使用する必要があります。これを行う最善の方法は何ですか?
メイン スレッドから (EndInvoke() の代わりに) ループでポーリングされる初期スレッド (スレッド セーフな実装) で保存キューを使用します。子スレッドは、メイン スレッドによって保存される NHibernate オブジェクトを挿入できます。
何らかのコールバック メカニズムを使用して、メイン スレッドでオブジェクトを保存/フラッシュします。WPF、Control.Invoke()、または BackgroundWorker の UI スレッド コールバックに類似したものはありますか?
保存/フラッシュ アクセスをロック (セッション) ブロックに入れますか? Save()/Flush() を実行しなくても、NHibernate オブジェクトを変更するとセッションが変更される可能性があるため、おそらく危険です。
または、データベースのオーバーヘッドを考慮して、各スレッドの個別のセッションに同じオブジェクトをロードし、メインスレッドでそれらを削除して再ロードしてから、再度変更を行う必要がありますか? [編集: オブジェクトの同時実行/古いオブジェクトのリスクによる悪い「解決策」]
また、アプリケーションには NHibernate の上にビジネス ロジック層があり、同様のオブジェクトがありますが、それ自体の Save() コマンドでそのプロパティ値を NHibernate オブジェクトに送信し、それらを変更して NHibernate Save()/Flush() をすぐに実行することも考慮してください。 .
編集: NHibernate オブジェクトに対する読み取り操作によってセッションが変更される可能性があることが重要です。遅延読み込み、特定の条件下でのチルレン コレクションの変更です。そのため、NHibernate オブジェクトへのすべてのアクセスを同期するビジネス オブジェクト レイヤーを最上位に配置することをお勧めします。データベース操作がスレッドの最小限の時間 (主に時折のステータス設定) しかかからず、ほとんどが計算、監視、Web サービス アクセスなどにかかることを考慮すると、データ レイヤーの同期によるパフォーマンスの低下は無視できます。