Wie lösche ich ein untergeordnetes Objekt in NHibernate?


79

Ich habe ein übergeordnetes Objekt, das eine Eins-zu-Viele-Beziehung zu einer IList von untergeordneten Objekten hat. Was ist der beste Weg, um die untergeordneten Objekte zu löschen? Ich lösche den Elternteil nicht. Mein übergeordnetes Objekt enthält eine IList mit untergeordneten Objekten. Hier ist die Zuordnung für die Eins-zu-Viele-Beziehung:

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

Wenn ich versuche, alle Objekte mit clear () aus der Sammlung zu entfernen, und dann SaveOrUpdate () aufrufe, wird folgende Ausnahme angezeigt:

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

Wenn ich versuche, die untergeordneten Objekte einzeln zu löschen und sie dann aus dem übergeordneten Objekt zu entfernen, wird eine Ausnahme angezeigt:

deleted object would be re-saved by cascade

Ich beschäftige mich zum ersten Mal mit dem Löschen von untergeordneten Objekten in NHibernate. Was mache ich falsch?

Bearbeiten: Nur zur Verdeutlichung - Ich versuche NICHT, das übergeordnete Objekt zu löschen, sondern nur die untergeordneten Objekte. Ich habe die Beziehung als eine zu viele auf dem Elternteil eingerichtet. Muss ich auch eine Viele-zu-Eins-Beziehung für die untergeordnete Objektzuordnung erstellen?

Antworten:


139

Sie erhalten den ersten Fehler, da der Standardbetriebsmodus von NHibernate beim Entfernen der Elemente aus der Sammlung darin besteht, die Zuordnung einfach zu unterbrechen. In der Datenbank versucht NHibernate, die Fremdschlüsselspalte in der untergeordneten Zeile auf null zu setzen. Da Sie in dieser Spalte keine Nullen zulassen, löst SQL Server den Fehler aus. Durch das Löschen der Sammlung wird das untergeordnete Objekt nicht unbedingt gelöscht. Eine Möglichkeit besteht darin, cascade = all-delete-orphan festzulegen. Dies informiert NHibernate darüber, dass die neu verwaisten Zeilen gelöscht werden sollen, anstatt die Fremdschlüsselspalte festzulegen.

Sie erhalten den zweiten Fehler, da beim Aufrufen von SaveOrUpdate NHibernate zuerst alle untergeordneten Objekte gelöscht werden. Da dann keine Beziehung als invers markiert ist, versucht NHibernate auch, die Fremdschlüsselspalte in Ihrer untergeordneten Tabelle auf null zu setzen. Da die Zeilen bereits gelöscht wurden, erhalten Sie den zweiten Fehler. Sie müssen auf einer Seite Ihrer Beziehung inverse = true setzen, um dies zu beheben. Dies erfolgt normalerweise auf der einen Seite (Primärschlüssel oder übergeordnete Seite). Wenn Sie dies nicht tun, nimmt NHibernate die entsprechenden Aktualisierungen für jede Seite der Beziehung vor. Leider ist es nicht angebracht, zwei Updates auszuführen.

Sie sollten immer eine Seite Ihrer Beziehungen als die umgekehrte Seite markieren. Abhängig davon, wie Sie codieren, müssen Sie möglicherweise Kaskadierung verwenden oder nicht. Wenn Sie die Vorteile von Löschvorgängen mit einem Schuss nutzen möchten, während Sie versuchen, Clear () zu verwenden, müssen Sie Ihre Kaskade definieren.


Nur zur Verdeutlichung: Ich habe gerade nur die Beziehung für das übergeordnete Objekt definiert. Ich habe versucht, die Kaskade für dieses Objekt auf "all" und "all-delete-orphan" zu setzen, wobei beide Male dieselben Ergebnisse erzielt wurden. Sie meinen, ich muss auch für das untergeordnete Objekt eine Eins-zu-Eins-Beziehung definieren?
Mark Struzinski

Und in diesem Fall gehört inverse = "true" zum übergeordneten oder untergeordneten Objekt?
Mark Struzinski

3
Es gehört zu der Klasse, die den Fremdschlüssel nicht enthält ... normalerweise zum übergeordneten Element.
pmlarocque

Vielen Dank für diese Erklärung, wieder hat mich ein umgekehrtes Problem aufgefangen, und es hat wirklich geholfen, es so erklärt zu sehen.
Mark Dickinson

Dies half wirklich bei der Lösung eines Problems, das ich hatte. Vielen Dank.
Robin Robinson

3

Gemäß Chucks Antwort habe ich mein Problem durch Hinzufügen von Inverse = true in der übergeordneten Seitenzuordnung gelöst:

Nachricht hat viele MessageSentTo:

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

Ich benutze Castle ActiveRecord. Danke Chuck.


2

Versuchen Sie, merge () anstelle von saveOrUpdate () zu verwenden. Stellen Sie außerdem sicher, dass Ihre Kaskade auf "All-Delete-Orphan" eingestellt ist und dass Ihre Eltern-Kind-Beziehung invertierbar ist (invers = true für das übergeordnete Element und dann ein Feld im untergeordneten Element, das die übergeordnete ID mit not-null = true ist). .


2

In unserem Beispiel haben wir Kategorien mit vielen Produkten, in denen ein Produkt nicht nullwertfähig ist.

Sie können das Problem umgehen, indem Sie das Produkt löschen und vor dem Flush aus der Sammlung der Eltern entfernen. Wir suchen jedoch noch nach einer besseren Lösung für dieses Problem.

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

Hoffe es hilft trotzdem


1
Dies erfordert jedoch ein anderes Repository.
UpTheCreek

0

Ändern Sie den Kaskadenattributwert von "all" in "all-delete-orphan".


Versuchte dies. Ich bekomme immer noch die gleiche Ausnahme.
Mark Struzinski

16
all-delete-orphan löscht die untergeordneten Elemente, wenn Sie das übergeordnete Element löschen. Es hat nichts damit zu tun, die Kinder zu löschen, nach denen dieser Typ fragt.
Kyle West

-3

Setzen Sie in Ihrer Zuordnung für die Spalte, die das Problem verursacht, Not-Null = true. Ich bin mir jedoch nicht sicher über die genaue Syntax (sorry).


Ok, ich habe die Spalte in der Zuordnung des untergeordneten Objekts auf not-null = "true" gesetzt. Also rufe ich die clear () -Methode in der IList von untergeordneten Objekten auf und versuche dann ein SaveOrUpdate (). Ich erhalte immer noch den Fehler "Der Wert NULL kann nicht in die Spalte eingefügt werden". Mache ich noch etwas falsch
Mark Struzinski

Auf welche Spalte wird der Fehler geworfen? Wie genau löschen Sie die Kinder in Ihrem Modell?
Kyle West

Ich rufe die clear () -Methode für das untergeordnete Ilist-Objekt in meinem übergeordneten Objekt auf. Dann rufe ich SaveOrUpdate () auf dem übergeordneten Element auf.
Mark Struzinski
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.