webentwicklung-frage-antwort-db.com.de

Wie lösche ich untergeordnete Entitäten vor Entity Framework CF?

Ich versuche, EF-Code-first zu verwenden, um einen Datenbanksatz (deleteMe) und dessen Kinder (deleteMe.Prices) zu löschen.

foreach (var deleteMe in deleteThese)
{ 
   // Delete validation
   if(CanDeleteItem(deleteMe.ItemId))
   {
      db.Entry(deleteMe).State = EntityState.Deleted;

      foreach (var item in deleteMe.Prices)
      {
         db.Entry(item).State = EntityState.Deleted; // cascade delete
      }
   }
}
db.SaveChanges();

Entity Framework scheint jedoch nicht zu verfolgen, dass die untergeordneten Datensätze vor dem übergeordneten Element gelöscht werden sollten. Ich bekomme den Fehler:

Die DELETE-Anweisung steht in Konflikt mit der REFERENCE-Einschränkung "ItemPrice_Item".
Der Konflikt trat in der Datenbank "DEVDB", Tabelle "dbo.ItemPrices", .__ auf. Spalte 'Item_ItemId'.
Die Anweisung wurde beendet.

Wie würde ich diesen Löschvorgang in EF ausführen?

21
quakkels

Ich fand schließlich eine kurze Zeile, die es für mich tun würde:

foreach (var deleteMe in deleteThese)
{ 
   // Delete validation
   if(CanDeleteItem(deleteMe.ItemId))
   {
      ///
      deleteMe.Prices.ToList().ForEach(p => db.ItemPrices.Remove(p));
      ///

      db.Entry(deleteMe).State = EntityState.Deleted;
   }
}
db.SaveChanges();
33
quakkels

EF6

context.Children.RemoveRange(parent.Children)
10
Eng. Samer T

Kaskadenlöschung in EF ist abhängig von der Kaskadenlöschung, die in der Datenbank in der Datenbank konfiguriert ist. Wenn Sie in der Datenbank keine Kaskadenlöschung konfiguriert haben, müssen Sie zuerst alle Artikelpreise in Ihre Anwendung laden und sie als gelöscht markieren.

8
Ladislav Mrnka

Nun, die einfachste Lösung wäre, zuerst die Preise zu durchlaufen und die Änderungen zum Speichern aufzurufen, dann den zu löschenden Eintrag für deleteMe festzulegen und die Änderungen erneut aufzurufen. Haben Sie jedoch Folgendes überprüft: Entity-Framework-Code zuerst mit cascade ? Es scheint, was du willst.

Neugierig, auch, warum Sie die Entitäten nicht einfach aus dem Kontext entfernen, sondern den Eintrittsstatus setzen?

Eine weitere Option ist die Einstellung der Kaskade delete http://blogs.msdn.com/b/alexj/archive/2009/08/19/tip-33-how-cascade-delete-really-works-in-ef.aspx

Tun Sie so etwas (nicht getestet, aber hoffentlich erhalten Sie die Jist):

using (TransactionScope scope = new TransactionScope())
{    
    foreach (var deleteMe in deleteThese)
    { 
   // Delete validation
      if(CanDeleteItem(deleteMe.ItemId))
      {

         foreach (var item in deleteMe.Prices)
         {
            db.Entry(item).State = EntityState.Deleted; // cascade delete
         }
         db.SaveChanges();

         db.Entry(deleteMe).State = EntityState.Deleted;


     }
   }
   db.SaveChanges();
   scope.Complete();
}     

Zusätzlich können Sie anrufen:

db.Prices.Remove(item);

und 

db.DeleteMes.Remove(deleteMe);

anstatt den Eintrittsstatus festzulegen. Nicht sicher, ob hinter den Kulissen ein Unterschied besteht.

5
SventoryMang

Cascade-Delete im Entity-Framework ist eine knifflige Angelegenheit, da Sie sich beim Löschen des Entity-Objektdiagramms sicher sein müssen. 

Wenn Sie versuchen, eine übergeordnete Entität in EF zu löschen, wird versucht, Löschanweisungen für alle untergeordneten Entitäten im aktuellen dbcontext auszuführen. Folglich werden keine untergeordneten Entitäten, die nicht geladen wurden, nicht initialisiert. Dies führt zu einem RDBMS-Laufzeitfehler, der die Fremdschlüsseleinschränkung verletzt. Stellen Sie sicher, dass alle abhängigen Entitäten vor dem Löschen in den aktuellen dbcontext geladen werden.

3
marvelTracker

Wenn Ihr Objekt auf sich selbst verweist, können Sie mit der unten stehenden Methode sowohl viele als auch viele untergeordnete Objekte löschen. Denken Sie daran, db.SaveChanges () anschließend 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);
    }
}
0
Matthew Hudson

Ich hatte ein ähnliches Problem und für mich sah es so aus, als hätte ich die Beziehung zwischen Eltern und Kind in ihren jeweiligen Klassen nicht richtig hergestellt.

Mein Fix bestand darin, der Child-Klasse die unten angegebenen Attribute für die Eigenschaft hinzuzufügen, die die ID des übergeordneten Objekts darstellte

    public class Child
    {
        [Key, Column(Order = 1)]
        public string Id { get; set; }

        [Key, ForeignKey("Parent"), Column(Order = 2)]  // adding this line fixed things for me
        public string ParentId {get; set;}
    }

    public class Parent
    {
        [Key, Column(Order = 1)]
        public string Id { get; set; }

        ...

        public virtual ICollection<Child> Children{ get; set; }
    }
0
Artie Leech