Ich erhalte diesen Fehler, wenn ich GetById () für eine Entität erhalte und dann die Sammlung untergeordneter Entitäten auf meine neue Liste setze, die aus der MVC-Ansicht stammt.
Der Vorgang ist fehlgeschlagen: Die Beziehung konnte nicht geändert werden, da eine oder mehrere der Fremdschlüsseleigenschaften nicht nullwertfähig sind. Wenn eine Beziehung geändert wird, wird die zugehörige Fremdschlüsseleigenschaft auf einen Nullwert gesetzt. Wenn der Fremdschlüssel keine Nullwerte unterstützt, muss eine neue Beziehung definiert, der Fremdschlüsseleigenschaft ein anderer Nicht-Nullwert zugewiesen oder das nicht verwandte Objekt gelöscht werden.
Ich verstehe diese Zeile nicht ganz:
Die Beziehung konnte nicht geändert werden, da eine oder mehrere der Fremdschlüsseleigenschaften nicht nullwertfähig sind.
Warum sollte ich die Beziehung zwischen zwei Entitäten ändern? Sie sollte während der gesamten Lebensdauer der gesamten Anwendung gleich bleiben.
Der Code, bei dem die Ausnahme auftritt, besteht darin, der vorhandenen übergeordneten Klasse einfach geänderte untergeordnete Klassen in einer Auflistung zuzuweisen. Dies würde hoffentlich das Entfernen von Kinderklassen, das Hinzufügen neuer Klassen und Änderungen ermöglichen. Ich hätte gedacht, Entity Framework kümmert sich darum.
Die Codezeilen können destilliert werden zu:
var thisParent = _repo.GetById(1);
thisParent.ChildItems = modifiedParent.ChildItems();
_repo.Save();
Antworten:
Sie sollten alte untergeordnete Elemente
thisParent.ChildItems
einzeln manuell löschen . Entity Framework erledigt das nicht für Sie. Es kann schließlich nicht entscheiden, was Sie mit den alten untergeordneten Elementen tun möchten - ob Sie sie wegwerfen möchten oder ob Sie sie behalten und anderen übergeordneten Entitäten zuweisen möchten. Sie müssen Entity Framework Ihre Entscheidung mitteilen. Aber eine dieser beiden Entscheidungen MÜSSEN Sie treffen, da die untergeordneten Entitäten nicht alleine leben können, ohne auf einen Elternteil in der Datenbank zu verweisen (aufgrund der Fremdschlüsseleinschränkung). Das sagt im Grunde die Ausnahme.Bearbeiten
Was würde ich tun, wenn untergeordnete Elemente hinzugefügt, aktualisiert und gelöscht werden könnten:
Hinweis: Dies wird nicht getestet. Es wird davon ausgegangen, dass die Sammlung untergeordneter Elemente vom Typ ist
ICollection
. (Normalerweise habe ichIList
und dann sieht der Code etwas anders aus.) Ich habe auch alle Repository-Abstraktionen entfernt, um es einfach zu halten.Ich weiß nicht, ob das eine gute Lösung ist, aber ich glaube, dass eine harte Arbeit in dieser Richtung geleistet werden muss, um alle Arten von Änderungen in der Navigationssammlung zu berücksichtigen. Ich würde mich auch freuen, einen einfacheren Weg zu sehen.
quelle
Der Grund dafür ist der Unterschied zwischen Zusammensetzung und Aggregation .
In der Komposition wird das untergeordnete Objekt erstellt, wenn das übergeordnete Objekt erstellt wird, und wird zerstört, wenn das übergeordnete Objekt zerstört wird . Die Lebensdauer wird also von den Eltern gesteuert. zB Ein Blog-Beitrag und seine Kommentare. Wenn ein Beitrag gelöscht wird, sollten seine Kommentare gelöscht werden. Es macht keinen Sinn, Kommentare für einen Beitrag zu haben, der nicht existiert. Gleiches gilt für Bestellungen und Bestellartikel.
In der Aggregation kann das untergeordnete Objekt unabhängig von seinem übergeordneten Objekt vorhanden sein . Wenn das übergeordnete Objekt zerstört wird, kann das untergeordnete Objekt weiterhin vorhanden sein, da es später einem anderen übergeordneten Objekt hinzugefügt werden kann. Beispiel: Die Beziehung zwischen einer Wiedergabeliste und den Titeln in dieser Wiedergabeliste. Wenn die Wiedergabeliste gelöscht wird, sollten die Songs nicht gelöscht werden. Sie können einer anderen Wiedergabeliste hinzugefügt werden.
Entity Framework unterscheidet Aggregations- und Zusammensetzungsbeziehungen wie folgt:
Für die Komposition: Es wird erwartet, dass das untergeordnete Objekt einen zusammengesetzten Primärschlüssel (ParentID, ChildID) hat. Dies ist beabsichtigt, da die IDs der Kinder im Bereich ihrer Eltern liegen sollten.
Für die Aggregation: Es wird erwartet, dass die Fremdschlüsseleigenschaft im untergeordneten Objekt nullwertfähig ist.
Der Grund für dieses Problem liegt darin, wie Sie Ihren Primärschlüssel in Ihrer untergeordneten Tabelle festgelegt haben. Es sollte zusammengesetzt sein, ist es aber nicht. Entity Framework betrachtet diese Zuordnung also als Aggregation. Wenn Sie also die untergeordneten Objekte entfernen oder löschen, werden die untergeordneten Datensätze nicht gelöscht. Die Zuordnung wird einfach entfernt und die entsprechende Fremdschlüsselspalte auf NULL gesetzt (sodass diese untergeordneten Datensätze später einem anderen übergeordneten Datensatz zugeordnet werden können). Da Ihre Spalte NULL nicht zulässt, erhalten Sie die von Ihnen erwähnte Ausnahme.
Lösungen:
1- Wenn Sie einen starken Grund haben, keinen zusammengesetzten Schlüssel verwenden zu wollen, müssen Sie die untergeordneten Objekte explizit löschen. Und dies kann einfacher erfolgen als die zuvor vorgeschlagenen Lösungen:
2- Andernfalls sieht Ihr Code aussagekräftiger aus, wenn Sie den richtigen Primärschlüssel für Ihre untergeordnete Tabelle festlegen:
quelle
Dies ist ein sehr großes Problem. Was in Ihrem Code tatsächlich passiert, ist Folgendes:
Parent
aus der Datenbank und erhalten eine angehängte EntitätJetzt hängt die Lösung wirklich davon ab, was Sie tun möchten und wie Sie es tun möchten?
Wenn Sie ASP.NET MVC verwenden, können Sie versuchen, UpdateModel oder TryUpdateModel zu verwenden .
Wenn Sie vorhandene Kinder nur manuell aktualisieren möchten, können Sie einfach Folgendes tun:
Das Anhängen ist eigentlich nicht erforderlich (
Modified
wenn Sie den Status auf festlegen , wird auch die Entität angehängt), aber ich mag es, weil es den Prozess offensichtlicher macht.Wenn Sie vorhandene ändern, vorhandene löschen und neue untergeordnete Elemente einfügen möchten, müssen Sie Folgendes tun:
quelle
.Clone()
. Denken Sie daran, dass aChildItem
andere untergeordnete Navigationseigenschaften hat? Aber möchten wir in diesem Fall nicht, dass der gesamte Untergraph an den Kontext angehängt wird, da wir erwarten würden, dass alle Unterkinder neue Objekte sind, wenn das Kind selbst neu ist? (Nun, könnte von Modell zu Modell unterschiedlich sein, aber nehmen wir den Fall an, dass die Unterkinder vom Kind "abhängig" sind, wie die Kinder vom Elternteil abhängig sind.)http://stackoverflow.com/questions/20233994/do-i-need-to-create-a-dbset-for-every-table-so-that-i-can-persist-child-entitie
Ich fand diese Antwort für den gleichen Fehler viel hilfreicher. Es scheint, dass EF es nicht mag, wenn Sie entfernen, es bevorzugt Löschen.
Sie können eine Sammlung von Datensätzen löschen, die an einen solchen Datensatz angehängt sind.
In diesem Beispiel ist für alle an eine Bestellung angehängten Detaildatensätze der Status "Löschen" festgelegt. (In Vorbereitung auf das Zurücksetzen aktualisierter Details als Teil eines Bestellupdates)
quelle
Ich habe keine Ahnung, warum die beiden anderen Antworten so beliebt sind!
Ich glaube, Sie haben zu Recht davon ausgegangen, dass das ORM-Framework damit umgehen sollte - schließlich verspricht es, dies zu erreichen. Andernfalls wird Ihr Domain-Modell durch Persistenzprobleme beschädigt. NHibernate schafft dies problemlos, wenn Sie die Kaskadeneinstellungen korrekt einrichten. In Entity Framework ist es auch möglich, dass sie nur erwarten, dass Sie beim Einrichten Ihres Datenbankmodells bessere Standards befolgen, insbesondere wenn sie ableiten müssen, welche Kaskadierung durchgeführt werden soll:
Sie müssen die Eltern-Kind-Beziehung korrekt definieren, indem Sie eine " identifizierende Beziehung " verwenden.
Wenn Sie dies tun, weiß Entity Framework, dass das untergeordnete Objekt vom übergeordneten Objekt identifiziert wird, und daher muss es sich um eine "Cascade-Delete-Orphans" -Situation handeln.
Anders als oben müssen Sie möglicherweise (aus NHibernate-Erfahrung)
anstatt die Liste vollständig zu ersetzen.
AKTUALISIEREN
@ Slaumas Kommentar erinnerte mich daran, dass getrennte Einheiten ein weiterer Teil des Gesamtproblems sind. Um dies zu lösen, können Sie einen benutzerdefinierten Modellordner verwenden, der Ihre Modelle erstellt, indem Sie versuchen, sie aus dem Kontext zu laden. Dieser Blog-Beitrag zeigt ein Beispiel dafür, was ich meine.
quelle
parent.ChildItems.Remove
anstelle von anrufen können_dbContext.ChildItems.Remove
. Es gibt immer noch (EF <= 6) keine integrierte Unterstützung von EF, um langen Code wie den in den anderen Antworten zu vermeiden.return context.Items.Find(id) ?? new Item()
Wenn Sie AutoMapper mit Entity Framework für dieselbe Klasse verwenden, tritt dieses Problem möglicherweise auf. Zum Beispiel, wenn Ihre Klasse ist
Dadurch wird versucht, beide Eigenschaften zu kopieren. In diesem Fall ist ClassBId nicht nullbar. Da AutoMapper kopiert
destination.ClassB = input.ClassB;
, verursacht dies ein Problem.Stellen Sie Ihren AutoMapper auf Ignorieren ein
ClassB
.quelle
Ich hatte gerade den gleichen Fehler. Ich habe zwei Tabellen mit einer übergeordneten untergeordneten Beziehung, aber ich habe in der Fremdschlüsselspalte in der Tabellendefinition der untergeordneten Tabelle eine "Kaskade beim Löschen" konfiguriert. Wenn ich also die übergeordnete Zeile (über SQL) in der Datenbank manuell lösche, werden die untergeordneten Zeilen automatisch gelöscht.
In EF funktionierte dies jedoch nicht. Der in diesem Thread beschriebene Fehler wurde angezeigt. Der Grund dafür war, dass in meinem Entitätsdatenmodell (edmx-Datei) die Eigenschaften der Zuordnung zwischen der übergeordneten und der untergeordneten Tabelle nicht korrekt waren. Die
End1 OnDelete
Option wurde wienone
folgt konfiguriert ("End1" in meinem Modell ist das Ende mit einer Multiplizität von 1).Ich habe die
End1 OnDelete
Option manuell aufCascade
und dann geändert . Ich weiß nicht, warum EF dies nicht erfassen kann, wenn ich das Modell aus der Datenbank aktualisiere (ich habe ein erstes Datenbankmodell).Der Vollständigkeit halber sieht mein zu löschender Code folgendermaßen aus:
Wenn ich keine Kaskadenlöschung definiert hätte, müsste ich die untergeordneten Zeilen manuell löschen, bevor ich die übergeordnete Zeile lösche.
quelle
Dies liegt daran, dass die untergeordnete Entität als "Geändert" anstatt als "Gelöscht" markiert ist.
Und die Änderung, die EF an der untergeordneten Entität vornimmt, wenn sie
parent.Remove(child)
ausgeführt wird, setzt einfach den Verweis auf die übergeordnete Entität aufnull
.Sie können den EntityState des Kindes überprüfen, indem Sie den folgenden Code in das Direktfenster von Visual Studio eingeben, wenn die Ausnahme nach der Ausführung auftritt
SaveChanges()
:Dabei sollte X durch die gelöschte Entität ersetzt werden.
Wenn Sie keinen Zugriff auf die
ObjectContext
auszuführende Funktion haben_context.ChildEntity.Remove(child)
, können Sie dieses Problem beheben , indem Sie den Fremdschlüssel zu einem Teil des Primärschlüssels in der untergeordneten Tabelle machen.Auf diese Weise
parent.Remove(child)
markiert EF bei der Ausführung die Entität korrekt als gelöscht.quelle
Diese Art von Lösung hat den Trick für mich getan:
Es ist wichtig zu sagen, dass dadurch alle Datensätze gelöscht und erneut eingefügt werden. Aber für meinen Fall (weniger als 10) ist es in Ordnung.
Ich hoffe, es hilft.
quelle
Ich bin heute auf dieses Problem gestoßen und wollte meine Lösung teilen. In meinem Fall bestand die Lösung darin, die untergeordneten Elemente zu löschen, bevor das übergeordnete Element aus der Datenbank abgerufen wurde.
Zuvor habe ich es wie im folgenden Code gemacht. Ich werde dann den gleichen Fehler in dieser Frage aufgelistet bekommen.
Was für mich funktioniert hat, ist, zuerst die untergeordneten Elemente mit der parentId (Fremdschlüssel) abzurufen und diese Elemente dann zu löschen. Dann kann ich das übergeordnete Element aus der Datenbank abrufen und zu diesem Zeitpunkt sollte es keine untergeordneten Elemente mehr enthalten und ich kann neue untergeordnete Elemente hinzufügen.
quelle
Sie müssen die ChildItems-Auflistung manuell löschen und neue Elemente anhängen:
Danach können Sie die DeleteOrphans-Erweiterungsmethode aufrufen, die mit verwaisten Entitäten umgehen kann (sie muss zwischen den DetectChanges- und SaveChanges-Methoden aufgerufen werden).
quelle
context.DetectChanges();
.Ich habe diese und viele andere Lösungen ausprobiert, aber keine davon hat ganz geklappt. Da dies die erste Antwort auf Google ist, füge ich hier meine Lösung hinzu.
Die Methode, die für mich gut funktionierte, bestand darin, während der Commits Beziehungen aus dem Bild zu entfernen, sodass EF nichts vermasseln konnte. Dazu habe ich das übergeordnete Objekt im DBContext erneut gefunden und gelöscht. Da die Navigationseigenschaften des wiedergefundenen Objekts alle null sind, werden die Beziehungen der Kinder während des Festschreibens ignoriert.
Beachten Sie, dass dies voraussetzt, dass die Fremdschlüssel mit ON DELETE CASCADE eingerichtet wurden. Wenn also die übergeordnete Zeile entfernt wird, werden die untergeordneten Zeilen von der Datenbank bereinigt.
quelle
Ich habe Moshs Lösung verwendet , aber es war mir nicht klar, wie ich den Kompositionsschlüssel zuerst korrekt in Code implementieren sollte.
Hier ist also die Lösung:
quelle
Ich hatte das gleiche Problem, aber ich wusste, dass es in anderen Fällen in Ordnung war, also reduzierte ich das Problem auf Folgendes:
Alles, was ich tun musste, war, die ParentId zu einem Teil der zusammengesetzten PK zu machen, um anzuzeigen, dass die Kinder ohne Eltern nicht existieren können. Ich habe das DB-first-Modell verwendet, die PK hinzugefügt und die parentId-Spalte als EntityKey markiert (daher musste ich sie sowohl in DB als auch in EF aktualisieren - nicht sicher, ob EF allein ausreichen würde).
Wenn Sie einmal darüber nachgedacht haben, ist es eine sehr elegante Unterscheidung, die EF verwendet, um zu entscheiden, ob Kinder ohne Eltern "sinnvoll" sind (in diesem Fall löscht Clear () sie nicht und löst eine Ausnahme aus, es sei denn, Sie setzen die ParentId auf etwas anderes / Besonderes ) oder - wie in der ursprünglichen Frage - erwarten wir, dass die Elemente gelöscht werden, sobald sie vom übergeordneten Element entfernt werden.
quelle
Dieses Problem tritt auf, weil wir versuchen, die übergeordnete Tabelle zu löschen, obwohl noch untergeordnete Tabellendaten vorhanden sind. Wir lösen das Problem mit Hilfe der Kaskadenlöschung.
Im Modell Create-Methode in der dbcontext-Klasse.
Danach in unserem API-Aufruf
Kaskadenlöschoption Löschen Sie die übergeordnete sowie die übergeordnete untergeordnete untergeordnete Tabelle mit diesem einfachen Code. Versuchen Sie es auf diese einfache Weise.
Entfernen Sie den Bereich, der zum Löschen der Liste der Datensätze in der Datenbank verwendet wurde. Danke
quelle
Ich löste auch mein Problem mit Mosh Antwort und ich dachte PeterB Antwort ein wenig war , da es eine ENUM als Fremdschlüssel verwendet. Denken Sie daran, dass Sie nach dem Hinzufügen dieses Codes eine neue Migration hinzufügen müssen.
Ich kann diesen Blog-Beitrag auch für andere Lösungen empfehlen:
http://www.kianryan.co.uk/2013/03/orphaned-child/
Code:
quelle
Mit der Lösung von Slauma habe ich einige allgemeine Funktionen erstellt, mit denen untergeordnete Objekte und Sammlungen von untergeordneten Objekten aktualisiert werden können.
Alle meine persistenten Objekte implementieren diese Schnittstelle
Damit habe ich diese beiden Funktionen in mein Repository implementiert
Um es zu benutzen, mache ich folgendes:
Hoffe das hilft
EXTRA: Sie können auch eine separate DbContextExtentions-Klasse (oder Ihre eigene Kontext-Inferface) erstellen:
und benutze es wie:
quelle
Ich hatte das gleiche Problem, als ich meinen Datensatz löschen wollte, als ein Problem aufgetreten ist. Diese Problemlösung besteht darin, dass Sie beim Löschen Ihres Datensatzes etwas verpassen, bevor Sie den Header / Master-Datensatz löschen, für den Sie in den Code schreiben müssen Löschen Sie die Details vor dem Header / Master. Ich hoffe, Ihr Problem wird behoben.
quelle
Ich habe dieses Problem vor einigen Stunden kennengelernt und alles versucht, aber in meinem Fall war die Lösung anders als oben aufgeführt.
Wenn Sie eine bereits abgerufene Entität aus der Datenbank verwenden und versuchen, die untergeordneten Elemente zu ändern, tritt der Fehler auf. Wenn Sie jedoch eine neue Kopie der Entität aus der Datenbank erhalten, sollten keine Probleme auftreten. Verwenden Sie dies nicht:
Benutze das:
quelle