Jak usunąć obiekt podrzędny w NHibernate?

79

Mam obiekt nadrzędny, który ma relację jeden do wielu z IListą obiektów potomnych. Jaki jest najlepszy sposób na usunięcie obiektów podrzędnych? Nie usuwam rodzica. Mój obiekt nadrzędny zawiera IList obiektów podrzędnych. Oto mapowanie relacji jeden do wielu:

<bag name="Tiers" cascade="all">
  <key column="mismatch_id_no" />
  <one-to-many class="TGR_BL.PromoTier,TGR_BL"/>
</bag>

Jeśli spróbuję usunąć wszystkie obiekty z kolekcji za pomocą clear (), a następnie wywołam SaveOrUpdate (), otrzymam ten wyjątek:

System.Data.SqlClient.SqlException: Cannot insert the value NULL into column

Jeśli spróbuję indywidualnie usunąć obiekty podrzędne, a następnie usunę je z rodzica, pojawia się wyjątek:

deleted object would be re-saved by cascade

Po raz pierwszy mam do czynienia z usuwaniem obiektów podrzędnych w NHibernate. Co ja robię źle?

edycja: tylko dla wyjaśnienia - NIE próbuję usunąć obiektu nadrzędnego, tylko obiekty podrzędne. Mam związek z rodzicem jako jeden do wielu. Czy muszę również utworzyć relację wiele do jednego w mapowaniu obiektów podrzędnych?

Mark Struzinski
źródło

Odpowiedzi:

139

Otrzymujesz pierwszy błąd, ponieważ po usunięciu pozycji z kolekcji domyślnym trybem działania NHibernate jest po prostu zerwanie skojarzenia. W bazie danych NHibernate próbuje ustawić kolumnę klucza obcego w wierszu podrzędnym na wartość null. Ponieważ nie zezwalasz na wartości null w tej kolumnie, SQL Server zgłasza błąd. Wyczyszczenie kolekcji niekoniecznie spowoduje usunięcie obiektu podrzędnego, ale jednym ze sposobów jest ustawienie cascade = all-delete-orphan. Informuje to NHibernate, że powinien usunąć nowo osierocone wiersze zamiast ustawiać kolumnę klucza obcego.

Otrzymujesz drugi błąd, ponieważ wywołanie SaveOrUpdate NHibernate najpierw usuwa wszystkie obiekty podrzędne. Następnie, ponieważ żadna z relacji nie jest oznaczona jako odwrotna, NHibernate próbuje również ustawić kolumnę klucza obcego w tabeli podrzędnej na wartość null. Ponieważ wiersze zostały już usunięte, pojawi się drugi błąd. Aby to naprawić, musisz ustawić inverse = true po jednej stronie relacji. Odbywa się to zwykle po jednej stronie (klucz podstawowy lub nadrzędny). Jeśli tego nie zrobisz, NHibernate dokona odpowiednich aktualizacji dla każdej strony relacji. Niestety, uruchomienie dwóch aktualizacji nie jest właściwe.

Zawsze powinieneś oznaczać jedną stronę swoich relacji jako odwrotną stronę. W zależności od tego, jak kodujesz, może być konieczne użycie kaskadowania. Jeśli chcesz skorzystać z usuwania jednego strzału, tak jak próbujesz to zrobić za pomocą Clear (), musisz zdefiniować swoją kaskadę.

Gdakanie
źródło
Dla wyjaśnienia - mam teraz tylko relację zdefiniowaną w obiekcie Parent. Próbowałem ustawić kaskadę tego obiektu na „wszystko” i „wszystko-usuń-osierocone” z tymi samymi wynikami za każdym razem. Masz na myśli, że muszę zdefiniować relację wiele do jednego również na obiekcie dziecka?
Mark Struzinski
Czy w tym przypadku inverse = "true" należy do obiektu nadrzędnego lub potomnego?
Mark Struzinski
3
należy do klasy, która nie posiada klucza obcego ... zwykle do rodzica.
pmlarocque
Dzięki za to wyjaśnienie, znowu złapał mnie odwrotny problem, a zobaczenie go wyjaśnionego w ten sposób naprawdę pomogło.
Mark Dickinson,
To naprawdę pomogło w rozwiązaniu problemu, który miałem. Dzięki.
Robin Robinson
3

Zgodnie z odpowiedzią Chucka rozwiązałem swój problem, dodając Inverse = true w mapowaniu strony rodzica:

Wiadomość zawiera wiele MessageSentTo:

[HasMany(typeof(MessageSentTo), Cascade = ManyRelationCascadeEnum.AllDeleteOrphan, Inverse = true)]
public IList<MessageSentTo> MessageSendTos
{
    get { return m_MessageSendTo; }
    set { m_MessageSendTo = value; }
}

Używam Castle ActiveRecord. Dziękuję Chuck.

hanuman0503
źródło
2

Spróbuj użyć merge () zamiast saveOrUpdate (). Upewnij się również, że kaskada jest ustawiona na all-delete-orphan i że relacja rodzic-dziecko jest odwracalna (inverse = true na rodzicu, a następnie pole w dziecku, które jest identyfikatorem rodzica z not-null = true) .

Elie
źródło
2

W naszym przykładzie mamy kategorie z wieloma produktami, w których produkt nie dopuszcza wartości null.

Możesz obejść ten problem, usuwając produkt i usuwając go z kolekcji rodzica przed usunięciem, ale wciąż szukamy lepszego rozwiązania tego problemu.

product = pRepo.GetByID(newProduct.ProductID);
product.Category.Products.Remove(product);
pRepo.Delete(product);

Mam nadzieję, że to i tak pomoże

Liath
źródło
1
Wymaga to jednak innego repozytorium.
UpTheCreek
0

Zmień wartość atrybutu kaskadowego z „all” na „all-delete-orphan”.

Thomas Tekavec
źródło
Próbowałem tego. Nadal mam ten sam wyjątek.
Mark Struzinski
16
all-delete-orphan usuwa dzieci, gdy usuniesz rodzica. nie ma to nic wspólnego z usuwaniem dzieci, o które pyta ten facet.
Kyle West
-3

ustaw Not-Null = true w mapowaniu w kolumnie powodującej problem. Nie jestem jednak pewien dokładnej składni (przepraszam).

Kyle West
źródło
Ok, ustawiłem kolumnę na not-null = "true" w mapowaniu obiektu podrzędnego. Dlatego wywołuję metodę clear () na IList obiektów potomnych, a następnie próbuję SaveOrUpdate (). Nadal pojawia się błąd „Nie można wstawić wartości NULL do kolumny”. Czy robię coś innego nie tak?
Mark Struzinski
w której kolumnie jest zgłaszany błąd? jak dokładnie usuwasz dzieci ze swojego modelu?
Kyle West
Wywołuję metodę clear () na obiekcie podrzędnym Ilist w moim obiekcie nadrzędnym. Następnie wywołuję SaveOrUpdate () na rodzicu.
Mark Struzinski