2009-04-27 1 views
8

私はnHibernateの新機能で、WebアプリケーションフォームPOSTから切り離されたオブジェクトを更新する適切な方法について私の頭を掴みようとしています。私たちはを配置しているasp.net POSTアクションメソッドからnhibernateエンティティを更新する適切な方法は何ですか?

<bag name="PlannedSlices" inverse="true" cascade="all-delete-orphan"> 
     <key column="JobNumber" /> 
     <one-to-many class="SliceClass" /> 
</bag> 

:このような何かをマッピングし、私は更新しようとしているオブジェクトは、(とりわけ)子オブジェクトのIListのを含んでい

を(私たちは、ASP.NET MVCを使用しています) MVC編集ビューフォームは、投稿されたときに私たちのアクションメソッドがオブジェクト(リスト<>子アイテムを含む)に渡されるようにします。

私たちの素朴な試みは、ポストアクションメソッドはsession.SaveOrUpdate(parentObject)を行い、parentObjectはデフォルトモードでビューフォームからスクレイプされていますバインダー。

これは、次のいずれかのシナリオのため正常に動作するようです:

  • 新しい子を追加する
  • 親のプロパティを変更する新しい親オブジェクト
  • を作成するには、既存の子を変更
  • オブジェクト( オブジェクトnHibernateログを見ると、オブジェクトが新規か既存かを正しく確認し、適切なUPDATEまたはINSERTを発行することができます)

失敗するシナリオは次のとおりです。 - 子オブジェクトを削除します。つまり、IListにない場合、データベースから削除されません。例外も何もなく、削除されないだけです。

これは、削除が必要な子のリストを作成するためにnHibernateが実行する魔法は、分離されたインスタンスでは機能しないためです。

nHibernate(つまり、モデルバインダーオブジェクトを分離されたnHibernateインスタンスとして使用する)のこのようなアクションメソッドの簡単な例は見つかりませんでした - MS EF(例:http://stephenwalther.com/blog/archive/2009/02/27/chapter-5-understanding-models.aspx)変更されたプロパティをモデルにバインドされたオブジェクトから再ロードされたエンティティインスタンスにコピーするメソッド 'ApplyPropertyChanges'を使用しているようです。

私はモデルバインダーを持っていれば、子オブジェクトのコレクションを含む新しいオブジェクトを私に渡します。どのようにnHibernate経由で更新するべきですか?(ここで、 'update'には削除可能性があります子どもたちの)?

答えて

4

ここに、私があなたがやろうとしていることをする例があります。あなたがしようとしていることを誤解しているかどうか教えてください。次のマッピングで

public class Person 
{ 
    private IList<Pet> pets; 

    protected Person() 
    { } 

    public Person(string name) 
    { 
     Name = name; 
     pets = new List<Pet>(); 
    } 

    public virtual Guid Id { get; set; } 
    public virtual string Name { get; set; } 
    public virtual IEnumerable<Pet> Pets 
    { 
     get { return pets; } 
    } 

    public virtual void AddPet(Pet pet) 
    { 
     pets.Add(pet); 
    } 

    public virtual void RemovePet(Pet pet) 
    { 
     pets.Remove(pet); 
    } 
} 

public class Pet 
{ 
    protected Pet() 
    { } 

    public Pet(string name) 
    { 
     Name = name; 
    } 

    public virtual Guid Id { get; set; } 
    public virtual string Name { get; set; } 
} 

public class PersonMap : ClassMap<Person> 
    { 
     public PersonMap() 
     { 
      LazyLoad(); 
      Id(x => x.Id).GeneratedBy.GuidComb(); 
      Map(x => x.Name); 
      HasMany(x => x.Pets) 
        .Cascade.AllDeleteOrphan() 
        .Access.AsLowerCaseField() 
        .SetAttribute("lazy", "false"); 
     } 
    } 

    public class PetMap : ClassMap<Pet> 
    { 
     public PetMap() 
     { 
      Id(x => x.Id).GeneratedBy.GuidComb(); 
      Map(x => x.Name); 
     } 
    } 

このテスト:

[Test] 
    public void CanDeleteChildren() 
    { 
     Person person = new Person("joe"); 

     Pet dog = new Pet("dog"); 
     Pet cat = new Pet("cat"); 

     person.AddPet(dog); 
     person.AddPet(cat); 

     Repository.Save(person); 

     UnitOfWork.Commit(); 

     CreateSession(); 
     UnitOfWork.BeginTransaction(); 

     Person retrievedPerson = Repository.Get<Person>(person.Id); 
     Repository.Evict(retrievedPerson); 

     retrievedPerson.Name = "Evicted"; 

     Assert.AreEqual(2, retrievedPerson.Pets.Count()); 
     retrievedPerson.RemovePet(retrievedPerson.Pets.First()); 

     Assert.AreEqual(1, retrievedPerson.Pets.Count()); 

     Repository.Save(retrievedPerson); 

     UnitOfWork.Commit(); 

     CreateSession(); 
     UnitOfWork.BeginTransaction(); 

     retrievedPerson = Repository.Get<Person>(person.Id); 
     Assert.AreEqual(1, retrievedPerson.Pets.Count()); 
    } 

実行され、次のSQLを生成します。

以下 "ドメイン" のクラスを考えると

DeletingChildrenOfEvictedObject.CanDeleteChildren:Passed NHibernate:INSERT INTO [Person](名前、ID)VALUES(@ p0、@ p1); @ p0 = 'joe'、@ p1 = 'cd123fc8-6163-42a5-aeeb-9bf801013ab2'

NHibernate:INSERT INTO [ペット](名前、ID)VALUES(@ p0、@ p1); @ p0 = 'dog'、@ p1 = '464e59c7-74d0-4317-9c22-9bf801013abb'

NHibernate:INSERT INTO [ペット](名前、ID)VALUES(@ p0、@ p1); @ p0 = 'cat'、@ p1 = '010c2fd9-59c4-4e66-94fb-9bf801013abb'

NHibernate:UPDATE [ペット] SET Person_id = @ p0 WHERE Id = @ p1; @ P0 = 'cd123fc8-6163-42a5-aeeb-9bf801013ab2'、@ P1 = '464e59c7-74d0-4317-9c22-9bf801013abb'

NHibernateは:UPDATE [ペット] SET PERSON_ID = @ P0 ID = @ P1。 @ P0 = 'cd123fc8-6163-42a5-aeeb-9bf801013ab2'、@ P1 = '010c2fd9-59c4-4e66-94fb-9bf801013abb'

NHibernateは:[人から人Name5_0_としてId5_0_、person0_.Nameとしてperson0_.Idを選択] person0_どこに[email protected]; @ P0 = 'cd123fc8-6163-42a5-aeeb-9bf801013ab2'

NHibernateは:Person3_1_、Id1_としてpets0_.Id、Id6_0_としてpets0_.Id、pets0_.Name Name6_0_としてFROM [ペット] pets0_としてpets0_.Person_idを選択[email protected]; @ p0 = 'cd123fc8-6163-42a5-aeeb-9bf801013ab2'

NHibernate:UPDATE [Person] SET Name = @ p0 WHERE Id = @ p1; @P0 = 'Evicted'、@ p1 = 'cd123fc8-6163-42a5-aeeb-9bf801013ab2'

NHibernate:UPDATE [ペット] SET名= @ p0 WHERE Id = @ p1; @ p0 = 'dog'、@ p1 = '464e59c7-74d0-4317-9c22-9bf801013abb' NHibernate:UPDATE [ペット] SET Person_id = null WHERE Person_id = @ p0 AND Id = @ p1; @ P0 = 'cd123fc8-6163-42a5-aeeb-9bf801013ab2'、@ P1 = '010c2fd9-59c4-4e66-94fb-9bf801013abb'

NHibernateは:[ペット] ID = @ P0から削除します。 @ P0 = '010c2fd9-59c4-4e66-94fb-9bf801013abb'

NHibernateは:Id5_0_、FROM Name5_0_としてperson0_.Name [人] person0_ [email protected]としてperson0_.Idを選択します。 @ P0 = 'cd123fc8-6163-42a5-aeeb-9bf801013ab2'

NHibernateは:Person3_1_、Id1_としてpets0_.Id、Id6_0_としてpets0_.Id、pets0_.Name Name6_0_としてFROM [ペット] pets0_としてpets0_.Person_idを選択[email protected];あなたが行うことができるために必要なもの@ P0 = 'cd123fc8-6163-42a5-aeeb-9bf801013ab2' [ペット]。DELETE FROM

注...そう

は、手NHibernateはPersonオブジェクト(ですこの例)を変更したコレクションで置き換え、削除するものを決定できるはずです。 Cascade.AllDeleteOrphan()属性が設定されていることを確認してください。

+0

非常にありがとうございます - そこに多くの作業!残念ながら、私が苦労しているのは、2番目のセッション/トランザクション(すなわち私のPOST)で持っている 'Person'オブジェクトが、ModelBinderによって作成されたまったく新しいオブジェクトであり、いくつかのフィールドが変更され、少数の「子を削除」の呼び出しが行われました。私が探しているのは、新しいオブジェクトを取得し、その変更を取得したオブジェクトに適用して、nhが必要なSQLを処理できる方法だと思います。たぶんそれは存在しないかもしれません。 –

+1

私がその状況に対処する方法は、モデルバインダーによって作成された新しいオブジェクトをプレゼンテーションモデルオブジェクトとして扱うことです。更新してそのオブジェクトに更新を適用するオブジェクトを取得する(または、何らかの形で "永続化"クラスのインスタンスを作成する)必要があります。その後、そのオブジェクトをNHibernateに保存することができます。それは理にかなっていますか? –

+0

「手動」(つまり、プロパティ別、ループなど、子コレクションのもの)は、 '取り出した'オブジェクトのプロパティを 'プレゼンテーション(つまりPOST)'オブジェクトのプロパティで上書きしますか?必要な子の削除を手動で計算しますか?私が望んでいたよりももう少し仕事があるようです(そして、維持!)。モデルバインダーでは迷惑になるようなことはほとんどありませんが、実際にはフィールドを1つずつ処理しなければならないかのように、フォームレスポンスから直接引き出すことができます。しかし、助けてくれてありがとう。 –

1

ロブの答えは、「既存のアイテムを新しいセッションにロードしてからマージする」というアプローチをもっと綿密に見せてくれると確信しました。もちろん、私が望んでいたものをそのまま実行するように見えるISession.Mergeがあります。新しいオブジェクトを作成し、それを第2セッションにリロードされた前任者とマージします。

私は尋ねようとした質問への答えは、 "既存のエンティティをリロードし、新しいエンティティで 'ISession.Merge'を呼び出すことです。"

関連する問題