Reputation: 17176
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.
Upvotes: 86
Views: 109273
Reputation: 7208
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.
Upvotes: 12
Reputation: 1316
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);
}
}
Upvotes: 1
Reputation: 3190
In EF6 a faster way to do the operation is...
context.Children.RemoveRange(parent.Children)
Upvotes: 117
Reputation: 177133
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();
}
Upvotes: 73