2009-04-27 17 views
8

Jestem nowy dla nHibernate i staram się zorientować się we właściwy sposób, aby zaktualizować odłączone obiekty z formularza aplikacji POST. (Używamy ASP.NET MVC)Jaki jest właściwy sposób aktualizowania obiektu nhibernate z metody akcji POST ASP.net?

obiektu Próbuję zaktualizować zawiera (między innymi) IList obiektów podrzędnych, odwzorowane coś takiego:

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

Mamy zorganizowali Formularz edycji MVC, tak aby po wysłaniu z powrotem nasza metoda działania została przekazana jako przedmiot (włączając w to listę <> przedmiotów podrzędnych) Prawidłowo przesuniemy wszystkie identyfikatory podmiotów za pomocą formularza:

Nasza naiwna próba metoda post action wykonuje sesję.SaveOrUpdate (parentObject) z parentObject, który został skrobany z formularza widoku przez domyślny tryb Lbinder.

To wydaje się działać dobrze dla jednego z następujących scenariuszy:

  • tworzenia nowego obiektu nadrzędnego
  • Modyfikowanie właściwości jednostki dominującej
  • Dodawanie nowych obiektów podrzędnych
  • Modyfikacja istniejących obiektów podrzędnych (Patrząc na dzienniki nHibernate, widzę, że poprawnie ustalono, czy obiekty są nowe czy istniejące, i wydaje odpowiednie UPDATE lub INSERT)

Scenariusz, który zawodzi, to: - Usuwanie obiektów podrzędnych - tj. Jeśli nie znajdują się one w IList, nie są usuwane z bazy danych. Nie ma wyjątku ani niczego, po prostu nie są usuwane.

Rozumiem, że dzieje się tak dlatego, że magia, którą nHibernate wykonuje w celu utworzenia listy dzieci wymagających usunięcia, nie działa z odłączonymi instancjami.

Nie byłem w stanie znaleźć prostego przykładu tego, jak powinna wyglądać ta metoda działania za pomocą nHibernate (tj. Używając obiektu wiążącego model jako zdemontowanej instancji nHiblera) - przykłady oparte na MS EF (np. http://stephenwalther.com/blog/archive/2009/02/27/chapter-5-understanding-models.aspx) wydaje się używać metody "ApplyPropertyChanges", aby skopiować zmienione właściwości z obiektu powiązanego z modelem do ponownie załadowanej instancji obiektu.

Po tym wszystkim, pytanie jest dość proste - jeśli mam segregator modelu, który daje mi nowy obiekt, który zawiera kolekcje obiektów podrzędnych, jak mam to zaktualizować za pomocą nHibernate, (gdzie "aktualizacja" obejmuje prawdopodobnie usunięcie dzieci)?

Odpowiedz

4

Oto przykład, który robi to, co myślę, że próbujesz zrobić. Daj mi znać, jeśli źle zrozumiałem, co próbujesz zrobić.

Biorąc pod uwagę następujące "domena" klas:

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; } 
} 

z następującym mapowaniem:

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); 
     } 
    } 

Ten test:

[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()); 
    } 

biegnie i generuje następujący SQL:

DeletingChildrenOfEvictedObject.CanDeleteChildren: Passsed NHibernate: INSERT INTO [Osoba] (Name, Id) VALUES (@ p0, @ p1); @ p0 = 'joe', @ p1 = 'cd123fc8-6163-42a5-aeeb-9bf801013ab2'

NHibernate: INSERT INTO [Zwierzę] (Imię, ID) WARTOŚCI (@ p0, @ p1); @ p0 = 'pies', @ p1 = '464e59c7-74d0-4317-9c22-9bf801013abb'

NHibernate: INSERT INTO [Zwierzę] (Imię, ID) WARTOŚCI (@ p0, @ p1); @ p0 = 'cat', @ p1 = '010c2fd9-59c4-4e66-94fb-9bf801013abb'

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

NHibernate aktualizowania [Zwierzę] ustawioną Person_id = @ P0 gdzie id = @ p1; @ P0 'cd123fc8-6163-42a5-aeeb-9bf801013ab2' @ P1 = '010c2fd9-59c4-4e66-94fb-9bf801013abb'

NHibernate SELECT person0_.Id jak Id5_0_, person0_.Name jako Name5_0_ Z [osoby ] person0_ WHERE [email protected]; @ P0 'cd123fc8-6163-42a5-aeeb-9bf801013ab2'

NHibernate SELECT pets0_.Person_id jak Person3_1_, pets0_.Id jako Id1_, pets0_.Id jako Id6_0_, pets0_.Name jako Name6_0_ Z [Zwierzę] pets0_ GDZIE [email protected]; @ p0 = 'cd123fc8-6163-42a5-aeeb-9bf801013ab2'

NHibernate: UPDATE [Osoba] SET Name = @ p0 WHERE Id = @ p1; @ p0 = 'Eksmitowane', @ p1 = 'cd123fc8-6163-42a5-aeeb-9bf801013ab2'

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

NHibernate: Usuń z [Zwierzę] gdzie id = @ P0; @ P0 '010c2fd9-59c4-4e66-94fb-9bf801013abb'

NHibernate SELECT person0_.Id jak Id5_0_, person0_.Name jako Name5_0_ Z [osób] person0_ GDZIE [email protected]; @ P0 'cd123fc8-6163-42a5-aeeb-9bf801013ab2'

NHibernate SELECT pets0_.Person_id jak Person3_1_, pets0_.Id jako Id1_, pets0_.Id jako Id6_0_, pets0_.Name jako Name6_0_ Z [Zwierzę] pets0_ GDZIE [email protected]; @ P0 = 'cd123fc8-6163-42a5-aeeb-9bf801013ab2'

Uwaga DELETE FROM [ZWIERZĘ] ...

tak, co trzeba, aby móc robić to ręcznie NHibernate obiekt Person (w ten przykład) ze zmodyfikowanymi kolekcjami i powinien być w stanie określić, co usunąć. Upewnij się, że masz ustawiony atrybut Cascade.AllDeleteOrphan().

+0

Dziękuję bardzo za to - dużo pracy! Niestety, tym, z czym borykam się, jest to, że obiekt "Person", który mam w drugiej sesji/transakcji (tj. Mój POST) jest zupełnie nowym obiektem, który został stworzony przez ModelBinder, a nie pobranym obiektem, który miał zmodyfikowano kilka pól i wykonano kilka wywołań "delete child". Myślę, że to, czego szukam, to sposób na wzięcie tego nowego obiektu i zastosowanie jego zmian do pobranego obiektu, tak aby nh mógł następnie wyliczyć wymagany SQL. Może to po prostu nie istnieje. –

+1

Sposób, w jaki mam do czynienia z tą sytuacją, traktuje nowy obiekt utworzony przez model spoiwa jako obiekt modelu prezentacji. Nadal będziesz musiał pobrać obiekt (lub w jakiś sposób utworzyć instancję klasy "utrwalonej"), który chcesz zaktualizować i zastosować te aktualizacje do tego obiektu. Następnie możesz zapisać ten obiekt w NHibernate. Czy to ma sens? –

+0

Zatem "ręcznie" (tzn. Właściwość po obiekcie, z pętlami lub czymkolwiek innym w przypadku kolekcji podrzędnych), nadpisują właściwości "odzyskanego" obiektu właściwościami obiektu "prezentacja (tj. POST)"? I ręcznie obliczyć wymagane usunięcia dzieci? Wydaje się, że więcej pracy (i konserwacji!), Niż miałem nadzieję. Prawie nie wydaje się, że warto zajmować się modelarskim spinaczem, tak, jakbym miał do czynienia z polami jeden po drugim, równie dobrze mógłbym wyciągnąć je prosto z odpowiedzi formy. Dzięki za pomoc. –

1

Odpowiedź Roba przekonała mnie do bliższego przyjrzenia się "załadowaniu istniejącego przedmiotu do nowej sesji, a następnie scaleniu" i oczywiście jest ISession.Merge, który wydaje się robić dokładnie to, co chciałem, co ma świeży obiekt i połączyć go z jego poprzednikiem, który właśnie został ponownie załadowany do drugiej sesji.

Więc myślę, że odpowiedź na pytanie, które próbowałem zadać, to "przeładowanie istniejącego podmiotu, a następnie wywołanie" ISession.Merge "z nowym podmiotem."

Powiązane problemy