Jak usunąć potomne od jednego do wielu powiązanych rekordów w bazie danych EF code first?

Cóż, mam jeden do wielu pokrewny model:

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

Chcę wyczyścić Parent.Children i usunąć powiązane elementy podrzędne z bazy danych. Już próbowałem:

Klasa kontekstu bazy danych:

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

To działa dobrze, ale nadal mam zbędne rekordy w bazie danych z polami Parent_Id = null kiedy robię

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

W mojej klasie repozytorium. Również to samo zachowanie jest wtedy, gdy robię:

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

Z dodatkową Parent właściwością w Child Klasa

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

Or when I do

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

Z dodatkową właściwością Parent_Id w klasie Child

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

Więc, jak mogę skonfigurować kasowanie kaskadowe poprawnie? Albo jak mam usunąć te dziecięce byty? Zakładam, że to przypadkowe zadanie, ale coś mi umyka.

Author: Dmytro, 2013-05-20

5 answers

Kaskadowe usuwanie nie ma tutaj żadnego efektu, ponieważ nie usuwa się parent, ale po prostu wywołuje InsertOrUpdate. Poprawną procedurą jest usuwanie dzieci jeden po drugim, jak na przykład:

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();
}
 44
Author: Slauma,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-05-20 18:28:45

W EF6 szybszym sposobem wykonania operacji jest..

 context.Children.RemoveRange(parent.Children)
 91
Author: Sam Sippe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-05-29 23:53:11

Spróbuj zmienić na

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

Ponieważ wirtualny jest potrzebny do leniwego ładowania. jak wyjaśniono tutaj

Myślę, że twój rodzic.Dzieci.Wyczyść nie działa, ponieważ dzieci nie zostały załadowane
 3
Author: Kirsten Greed,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:18:20

Jeśli twój obiekt odnosi się do siebie, możesz usunąć zarówno wiele do wielu, jak i jedno do wielu dzieci za pomocą poniższej metody. Tylko pamiętaj, żeby zadzwonić do db.SaveChanges ():)

[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
Author: Matthew Hudson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-08-15 13:54:36

To się nazywa "kasowanie sierot".

Czy EF może automatycznie usuwać osierocone dane, jeśli rodzic nie zostanie usunięty?

Nie wiem jak to działa w EF6 ale w EF Core działa dobrze https://docs.microsoft.com/en-us/ef/core/saving/cascade-delete więc niekoniecznie trzeba usunąć rodzica kaskady do pracy.

Delete orphans examples

 1
Author: Konrad,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-05 21:10:58