2010-11-19 3 views
1

NHibernateプロジェクトで作業中で、一時的なエンティティの更新に関する質問があります。NHibernate - 'dirty'として特定のプロパティにフラグを付ける

次のように基本的なワークフローは、次のとおり

  1. DTO(投影)を作成し、クライアントにワイヤを介して送信。これは、エンティティからのプロパティの小さなサブセットを持っています。
  2. クライアントは変更されたDTOを返信します
  3. DTOプロパティを適切なエンティティにマップして、NHによってUPDATEステートメントを生成し実行することができます。私は問題を持っているところ
  4. 保存エンティティ

点4です。現在、私はsession.Merge()メソッドを使用してこのアップデートを達成することができますが、更新する前にまずdbからエンティティをロードする必要があります(2LCと仮定しない)。したがって、selectステートメントとupdateステートメントの両方が起動されます。

私がしたいのは、エンティティの一時的なインスタンスを作成し、DTOから新しい値をマップし、NHが変更したプロパティのみを使用してSQL文を生成させることです。エンティティIDとSET句に必要な値がすでにあるので、追加の選択は不要です。これはNHで可能ですか?

現在、session.Update()を使用すると、すべてのプロパティがupdate文に含まれ、DTOの一部ではない初期化されていないプロパティによって例外が発生します。

本質的に、どちらのエンティティプロパティがダーティであるかを指定する方法が必要なので、これらのみがアップデートに含まれています。

== EDITの==例えば

...

public class Person 
{ 
    public virtual int PersonId { get; set; } 
    public virtual string Firstname { get; set; } 
    public virtual string Nickname { get; set; }  
    public virtual string Surname { get; set; } 
    public virtual DateTime BirthDate { get; set; }  
} 

およびテストケース。

// Create the transient entity 
Person p = new Person() 
p.id = 1; 

using (ISession session = factory.OpenSession()) 
{ 
    session.Update(p); 

    // Update the entity – now attached to session  
    p.Firstname = “Bob”; 

    session.Flush(); 
} 

私は「UPDATE者SETファーストネーム= 'ボブのWHERE PERSONID = 1」のようなSQL文を生成するために期待していました。代わりに、BirthDateが初期化されていないため、DateTimeが範囲外の例外に遭遇します。 SQL文では必須ではないため、BirthDateは必要ありません。多分これは不可能でしょうか?事前に

==/EDIT ==

おかげで、 ジョン

答えて

4

ダイナミックアップデートはあなたが探しているものです。あなたのマッピングファイル(hbm.xml):

<class name="Foo" dynamic-update="true"> 
    <!-- remainder of your class map --> 

これは潜在的な問題の可能性があることに注意してください。 FirstNameまたはNicknameをnullにすることはできないというドメインロジックがあるとします。 (これを完全に作っている)2人がJon "Jonboy" Jonsonを同時に更新する。彼はファーストネームを削除します。 dynamic-updateがtrueであるため、updateステートメントはJonをnullにし、レコードは現在 "Jonboy" Jonsonになります。他の同時更新ではニックネームが削除されます。その意図はJon Jonboyです。しかしニックネームのヌルアウトだけがデータベースに送られます。 FirstNameまたはニックネームのないレコードが作成されました。dynamic-updateがfalseの場合、2回目の更新でJon Jonboyに設定されます。おそらく、これはあなたの状況には問題ではないかもしれませんが、dynamic-update = "true"を設定すると結果に影響があります。

更新:コードをありがとう。それが助けになりました。基本的な問題はNHibernateが十分な情報を持っていないことです。 session.Update(p)と言うと、NHibernateは、切断されたエンティティを現在のセッションに関連付ける必要があります。デフォルト以外のPKを持っています。 NHibernateはそれが更新であり、挿入ではないことを知っています。 session.Update(p)と言うと、NHibernateはそのエンティティ全体をダーティとみなしてデータベースに送ります。 (session.Merge(obj)を使用する場合、NHibernateはデータベースからエンティティを選択し、objをマージします)。これはあなたが実際に意味するものではありません。オブジェクトを現在のセッションに関連付けるが、それをきれいなものとしてマークしたい。 APIはやや直感的ではありません。以下のようにsession.Lock(obj、LockMode.None)を使用します。

using(var session = sessionFactory.OpenSession()) 
using(var tx = session.BeginTransaction()) { 
    var p = new Person {PersonId = 1}; 
    session.Lock(p, LockMode.None); // <-- This is the secret sauce! 
    p.Firstname = "Bob"; 
    // No need to call session.Update(p) since p is already associated with the session. 
    tx.Commit(); 
} 

(NB動的な更新=「真」が私のマッピングで指定されている。)これは、次のSQLになり

UPDATE Person 
SET Firstname = 'Bob' /* @p0_0 */ 
WHERE PersonId = 1 /* @p1_0 */ 
+0

だけでなく、これを使用した場合のパフォーマンスへの影響もあります、私は静的な更新を使用して、(全体的な)セッションファクトリーが構築されたときにnhibernateが更新ステートメントを準備する方法のためによりパフォーマンスが良いと信じています。 – DanP

+0

こんにちはジェームズ、応答ありがとう。エンティティに対してDynamicUpdate(Fluent Mapping)が設定されています。問題をより明確に示す例があるかもしれません。上記の編集セクションをご覧ください。 – John

+0

私はこのAPIが直感的ではないことに同意する必要があります;)私は '実世界'のアプリケーションでsession.Lock()を使ってテストしました。このジェームズのおかげで大変感謝しています。 – John

関連する問題