Nun, ich habe ein zu viele verwandte Modelle:
public class Parent
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Child> Children { get; set; }
}
public class Child
{
public int Id { get; set; }
public string ChildName { get; set; }
}
Ich möchte Parent.Children
verwandte untergeordnete Entitäten löschen und aus der Datenbank entfernen. Ich habe es bereits versucht:
Datenbankkontextklasse:
modelBuilder.Entity<Parent>()
.HasMany(p => p.Children)
.WithOptional()
.WillCascadeOnDelete(true);
Dies funktioniert einwandfrei, aber ich habe immer noch redundante Datensätze in der Datenbank mit Parent_Id = null
Feldern, wenn ich dies tue
parent.Children.Clear();
repository.InsertOrUpdate(parent);
in meiner Repository-Klasse. Das gleiche Verhalten ist auch, wenn ich:
modelBuilder.Entity<Parent>()
.HasMany(pr => pr.Children)
.WithOptional(ri => ri.Parent)
.WillCascadeOnDelete(true);
mit zusätzlicher Parent
Eigenschaft in der Child
Klasse
public class Child
{
...
public Parent Parent { get; set; }
...
}
oder wenn ich es tue
modelBuilder.Entity<Child>()
.HasOptional(p => p.Parent)
.WithMany(p => p.Children)
.HasForeignKey(p => p.Parent_Id)
.WillCascadeOnDelete(true);
mit zusätzlicher Parent_Id-Eigenschaft in der Child
Klasse
public class Child
{
...
public int Parent_Id { get; set; }
...
}
Wie kann ich das Löschen von Kaskaden korrekt konfigurieren? Oder wie soll ich diese untergeordneten Entitäten entfernen? Ich nehme an, das ist eine Gelegenheitsaufgabe, aber mir fehlt nur etwas.
c#
entity-framework
code-first
Dmytro
quelle
quelle
OnModelCreating()
? Wenn ich Ihre Entitäten und Ihren ersten Zuordnungsversuch kopiere und einfüge (undId
als Schlüsseleigenschaft für beide Entitäten festlege ), werden Löschvorgänge für mich korrekt kaskadiert.Antworten:
Das kaskadierende Löschen hat hier keine Auswirkung, da Sie das nicht löschen,
parent
sondern nur aufrufenInsertOrUpdate
. Das richtige Verfahren besteht darin, die Kinder einzeln zu löschen, wie zum Beispiel:using (var context = new MyContext()) { var parent = context.Parents.Include(p => p.Children) .SingleOrDefault(p => p.Id == parentId); foreach (var child in parent.Children.ToList()) context.Children.Remove(child); context.SaveChanges(); }
quelle
context.Children.RemoveRange(parent.Children.ToArray())
suchen , sagen Sie einfach so, dass der DbContext nicht jedes Mal, wenn Sie Remove aufrufen, so viel Arbeit erledigen muss. Dies ist möglicherweise kein großes Leistungsproblem beim Löschen, aber ich habe einen großen Unterschied beim Hinzufügen von Elementen nach dem anderen festgestellt, als ich das Hinzufügen von 100.000 Datensätzen mithilfecontext.Children.Add(child)
einer for-Schleife getestet habe .ON DELETE CASCADE
und DB-Modelle erneut generieren, umDeleteBehavior.Cascade
sie zu haben .In EF6 ist eine schnellere Möglichkeit, die Operation durchzuführen, ...
quelle
parent.Id == 10
und meineChild
Klasse 100 Felder hat und noch nicht in den Speicher geladen wurde, scheint die Verwendung ineffizient zu sein,RemoveRange
da das Programmparent.Children
standardmäßig mit allen 100 Feldern geladen werden muss .var child = context.Children.Single(f=>f.Id==childId); var parent = context.Parents.Single(f=>f.Id==child.ParentId);
Dies wird als "Löschen von Waisen" bezeichnet.
Kann EF verwaiste Daten automatisch löschen, wenn das übergeordnete Element nicht gelöscht wird?
Ich weiß nicht, wie es in EF6 funktioniert, aber in EF Core funktioniert es einwandfrei https://docs.microsoft.com/en-us/ef/core/saving/cascade-delete, sodass Sie das nicht unbedingt löschen müssen Eltern, damit Kaskaden funktionieren.
Beispiele für Waisenkinder löschen
quelle
Versuchen Sie, zu zu wechseln
public virtual ICollection<Child> Children { get; set; }
weil virtuell benötigt wird, um faul zu laden. wie hier erklärt
Ich denke, deine Eltern.Kinder.clear funktionieren nicht, weil die Kinder nicht geladen wurden
quelle
Wenn sich Ihr Objekt selbst referenziert, können Sie mit der folgenden Methode sowohl viele-zu-viele als auch eins-zu-viele untergeordnete Objekte löschen. Denken Sie daran, danach db.SaveChanges () aufzurufen :)
[HttpPost, ActionName("Delete")] [ValidateAntiForgeryToken] public ActionResult DeleteConfirmed(int id) { Object obj = this.db.Objects.Find(id); this.DeleteObjectAndChildren(obj); this.db.Objects.Remove(obj); this.db.SaveChanges(); return this.Json(new { success = true }); } /// <summary> /// This deletes an object and all children, but does not commit changes to the db. /// - MH @ 2016/08/15 14:42 /// </summary> /// <param name="parent"> /// The object. /// </param> private void DeleteObjectAndChildren(Object parent) { // Deletes One-to-Many Children if (parent.Things != null && parent.Things.Count > 0) { this.db.Things.RemoveRange(parent.Things); } // Deletes Self Referenced Children if (parent.Children != null && parent.Children.Count > 0) { foreach (var child in parent.Children) { this.DeleteObjectAndChildren(child); } this.db.Objects.RemoveRange(parent.Children); } }
quelle