現在のアプローチ
ASP.NET Web フォーム アプリ (Spring.NET と NHibernate を使用) には、多数の画面/ページにわたって詳細がキャプチャされる集約ルート ( Person ) があります。Personエンティティは、このワークフローに入る前に存在し、Personオブジェクト グラフに加えられたすべての変更はアトミックであるため、最終画面の送信時にのみデータベースにフラッシュする必要があります。
これを実現するために、NHibernate 3.2 を使用して最初のページに最初にPersonをデータベースから (遅延的に) ロードし、その後、プロセスをページングするときに、シリアライズされたPersonオブジェクト グラフを HTTP セッション変数にロードして保存します。
HTTP セッションからPersonを取得した後、現在の NHibernate セッションから切り離された状態になっているため、現在のセッションでUpdate()メソッドを呼び出して再接続します。
var sessionPerson = Session[PersonSessionName] as Person;
var currentSession = SessionFactory.GetCurrentSession();
currentSession.Update(sessionPerson);
注: Lock()を使用すると、例外がスローされ、「再関連付けされたオブジェクトにダーティ コレクションがある」ことが通知されました。
再接続すると、期待どおりにオブジェクト グラフをトラバースし、まだメモリに読み込まれていない子エンティティのデータをデータベースから取得できます。
マッピング ファイルのサブセット
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" auto-import="false" assembly="Domain" namespace=" TestApp.Domain">
<class name="Person" table="Person">
<id name="Id">
<generator class="TestApp.CustomNHibernateHiLoGenerator, TestApp.Core" />
</id>
<property name="Name" not-null="false" />
<bag name="PersonCountries" access="field.camelcase-underscore" cascade="all-delete-orphan">
<key column="PersonId" foreign-key="FK_ PersonCountry_Person" not-null="true" />
<one-to-many class="PersonCountry" />
</bag>
</class>
<class name="Country" table="Country">
<id name="Id">
<generator class="TestApp.CustomNHibernateHiLoGenerator, TestApp.Core" />
</id>
... No back reference to Person
</class>
</hibernate-mapping>
ドメイン
public class PersonCountry : Entity, ICloneable
{
// No properties of note
}
public class Person : Entity, ICloneable
{
public virtual string Name { get; set; }
public virtual IEnumerable<PersonCountry> PersonCountries { get; set; }
...
// More Properties
}
データベースへの変更のフラッシュ
.. // Code-behind
PricingService.Save(ProductContext.Pricing, forceMerge: true);
public class PricingService : IPricingService
{
[Transaction] // Spring.NET transaction
public Pricing Save(Pricing pricing, bool forceMerge = false)
{
if(forceMerge)
{
CurrentSession.Merge(entity);
}
else
{
CurrentSession.SaveOrUpdate(entity);
}
}
}
すべての変更をデータベースにフラッシュするときが来たら、Nameのみを変更すれば、変更は期待どおりに機能します。ただし、新しいCountry項目をPersonに追加すると、1 対多の関係でのMerge()のカスケードが次の例外で失敗します (奇妙なことに、Countryを削除しても問題なく動作します)。
NHibernate.StaleStateException: Batch update returned unexpected row count from update; actual row count: 0; Expected: 1
どんな助けでも大歓迎です。