How to remove child one to many related records in EF code first database?

Well, I have one-to-many related 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; }
}

What I want to do is clear Parent.Children and remove related child entities from database. I've already tried:

Database context class:

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

this works fine, but I still have redundant records in database with Parent_Id = null fields when I do

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

in my repository class. Also the same behavior is when I do:

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

with additional Parent property in Child class

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

with additional Parent_Id property in Child class

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

So, how can I configure cascade deleting correctly? Or how should I supposed to remove those child entities? I assume this is casual task but I'm just missing something.

97455 次浏览

Try changing to

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

because virtual is needed to get lazy loading. as explained here

I think your parent.Children.clear isnt working because the Children have not been loaded

Cascading delete has no effect here because you don't delete the parent but just call InsertOrUpdate. The correct procedure is to delete the children one-by-one, like so for example:

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

In EF6 a faster way to do the operation is...

 context.Children.RemoveRange(parent.Children)

If your object is self-referencing, you can delete both many-to-many and one-to-many children using the method below. Just remember to call db.SaveChanges() afterwards :)

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

This is called "deleting orphans".

Can EF automatically delete data that is orphaned, where the parent is not deleted?

I don't know how it works in EF6 but in EF Core it works fine https://learn.microsoft.com/en-us/ef/core/saving/cascade-delete so you don't necessarily need to delete the parent for cascades to work.

Delete orphans examples