web-dev-qa-db-fra.com

Comment supprimer l'enfant un à plusieurs enregistrements associés dans la première base de données de code EF?

Eh bien, j'ai un modèle associé un à plusieurs:

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; }
}

Ce que je veux faire est clair Parent.Children et supprimez les entités enfant associées de la base de données. J'ai déjà essayé:

Classe de contexte de base de données:

modelBuilder.Entity<Parent>()
            .HasMany(p => p.Children)
            .WithOptional()
            .WillCascadeOnDelete(true);

cela fonctionne bien, mais j'ai toujours des enregistrements redondants dans la base de données avec Parent_Id = null champs quand je le fais

parent.Children.Clear();
repository.InsertOrUpdate(parent);

dans ma classe de référentiel. Aussi le même comportement est quand je fais:

modelBuilder.Entity<Parent>()
            .HasMany(pr => pr.Children)
            .WithOptional(ri => ri.Parent)
            .WillCascadeOnDelete(true);

avec une propriété supplémentaire Parent dans Child class

public class Child
{
    ...
    public Parent Parent { get; set; }
    ...
}

ou quand je fais

modelBuilder.Entity<Child>()
            .HasOptional(p => p.Parent)
            .WithMany(p => p.Children)
            .HasForeignKey(p => p.Parent_Id)
            .WillCascadeOnDelete(true);

avec une propriété additionnelle Parent_Id dans la classe Child

public class Child
{
     ...
     public int Parent_Id { get; set; }
     ...
}

Alors, comment puis-je configurer la suppression en cascade correctement? Ou comment devrais-je supposer supprimer ces entités enfants? Je suppose que c'est une tâche occasionnelle, mais que quelque chose me manque.

60
Dmytro

La suppression en cascade n'a aucun effet ici car vous ne supprimez pas le parent, mais vous appelez simplement InsertOrUpdate. La procédure correcte consiste à supprimer les enfants un par un, comme par exemple:

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();
}
59
Slauma

En EF6, un moyen plus rapide d'opérer est ...

 context.Children.RemoveRange(parent.Children)
97
Sam Sippe

Cela s'appelle "supprimer des orphelins".

EF peut-il supprimer automatiquement les données orphelines où le parent n'est pas supprimé?

Je ne sais pas comment cela fonctionne dans EF6, mais dans EF Core, cela fonctionne très bien https://docs.Microsoft.com/en-us/ef/core/saving/cascade-delete Il n'est pas nécessaire de supprimer le parent pour que cascades fonctionne.

Supprimer les exemples d'orphelins

3
Konrad

Essayez de changer pour

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

car virtuel est nécessaire pour obtenir un chargement paresseux. comme expliqué ici

Je pense que votre parent.Children.clear ne fonctionne pas parce que les enfants n'ont pas été chargés

2
Kirsten Greed

Si votre objet fait référence à vous-même, vous pouvez supprimer les enfants plusieurs-à-plusieurs et un-à-plusieurs en utilisant la méthode ci-dessous. Rappelez-vous simplement d'appeler db.SaveChanges () après :)

[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);
    }
}
1
Matthew Hudson