16

現在のアプローチ

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

どんな助けでも大歓迎です。

4

1 に答える 1

6

有効な Id を持つすべてのエンティティは永続的であると見なされます。そのため、マージで更新しようとしますが、まだ保存されていないため失敗します。session.Flush()後に電話するsession.Update()

于 2012-12-13T20:03:38.803 に答える