Entity Framework Pitfalls: Cascade Deletes
Introduction
Entity Framework supports cascading deletes for all of its associations:
- One to one
- One to many
- Many to many
Meaning, when we remove an entity, it will automatically descend to its dependent entities (cascade) and delete them too.
Of course, cascading deletes of many to one really does not make much sense. The problem with cascade deletes configuration in EF is, sometimes it cannot be done with attributes, which is a mapping mechanism that most people like and use, and the different associations need different approaches.
One to One
For one to one relations, we need to use code mappings, say, we have an entity Master associated with a dependent entity Detail:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder
.Entity<Master>()
.HasOptional(x => x.Detail)
.WithRequired(x => x.Master)
.WillCascadeOnDelete(true);
base.OnModelCreating(modelBuilder);
}
Deleting a Master will also delete the Detail, but, of course, not the other way around. If you are curious, EF does this through an ON DELETE CASCADE INDEX, so it is done by the database, not by EF – more on this later.
One to Many
We just need to mark the one endpoint with a [Required] attribute, like in a Detail entity that is a child of a Master:
public class Detail
{
public int Id { get; set; }
[Required]
public virtual Master Master { get; set; }
}
This way, when we delete the Master entity, it will first delete all of its related Detail entities. This is also achieved through the automatically created ON DELETE CASCADE INDEX.
It is also certainly possible to achieve this using code configuration, but not really necessary – and more verbose:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder
.Entity<Master>()
.HasMany(x => x.Details)
.WithRequired(x => x.Master)
.WillCascadeOnDelete();
base.OnModelCreating(modelBuilder);
}
Many to Many
This one is the best: guess what, we don’t have to do anything, it just works out of the box!
Conclusion
Because Entity Framework relies on INDEXes to cascade the deletes, you cannot just change the code mappings after you created the model – either by adding attributes or through fluent configuration – and expect things to work. If you do, you will likely need to update the database. Do so by creating and applying a new migration. One thing that is still (as of EF 6.x) not supported is deleting orphans, but I already talked about it. EF Core will handle it properly.