Reader small image

You're reading from  Entity Framework Core Cookbook - Second Edition

Product typeBook
Published inNov 2016
PublisherPackt
ISBN-139781785883309
Edition2nd Edition
Right arrow
Author (1)
Ricardo Peres
Ricardo Peres
author image
Ricardo Peres

Ricardo Peres is a Portuguese developer, blogger, and book author and is currently a team leader at Dixons Carphone. He has over 20 years of experience in software development and his interests include distributed systems, architectures, design patterns, and .NET development. He won the Microsoft MVP award in 2015 and has held this title up to 2020. He also authored Entity Framework Core Cookbook – Second Edition and Mastering ASP.NET Core 2.0, and was a technical reviewer for Learning NHibernate 4 for Packt. He also contributed to Syncfusion's Succinctly collection, with titles on .NET development. Ricardo maintains a blog—Development With A Dot—where he writes about technical issues. You can catch up with him on Twitter at @rjperes75.
Read more about Ricardo Peres

Right arrow

Cascading entity deletes


Cascading deletes, if not configured properly, can cause cycles.

Problem

Cascade delete means that if an entity is deleted, all of its dependent entities will be removed too. This makes sense in those cases where a child cannot exist without its parent; imagine a blog and its posts, for example. This is how Entity Framework deletes these dependent entities:

  • If we are using a database engine that supports cascades in constraints (such as SQL Server) and Entity Framework was used to create the database, it will use them

  • If the dependent entities are not loaded, they will be deleted with a single DELETE statement

  • If we cannot delete them, we can set the relation property to NULL; unfortunately, this has to be done one by one for all the related entities

Problems start to arise if we let Entity Framework set up the cascade constraints in the database and there are cycles, meaning the deletion of a record cascades to other records, possibly in different tables, which in turn will trigger a deletion on a third level table, like in the following diagram:

Multi-level relationships

We cannot let SQL Server manage these cascade deletions through foreign key constraints because it would result in an error when the database is created.

How to solve it…

Cascade deletions are enabled by default for required relations but can be configured explicitly. For that, we use the fluent API:

protected override void OnModelCreating(
ModelBuilder modelBuilder)
{
    modelBuilder
      .Entity<Parent>()
      .HasMany(c => c.Children2)
      .WithOne(c => c.Parent)
      .OnDelete(DeleteBehavior.SetNull);
    base.OnModelCreating(modelBuilder);
}

And we also use this class model:

public class Parent
{
    public int Id { get; set; }
    public string Name { get; set; }
    [InverseProperty("Parent")]
    public ICollection<Child1> Children1 { get; set; }
    [InverseProperty("Parent")]
    public ICollection<Child2> Children2 { get; set; }
}
public class Child1
{
    public int Id { get; set; }
    public int Option { get; set; }
    [Required]
    public Parent Parent { get; set; }
    [InverseProperty("Child1")]
    public ICollection<GrandChild> GrandChildren 
    { get; set; }
}
public class Child2
{
    public int Id { get; set; }
    public string Text { get; set; }
    public Parent Parent { get; set; }
    [InverseProperty("Child2")]
    public ICollection<GrandChild> GrandChildren
    { get; set; }
}
public class GrandChild
{
    public int Id { get; set; }
    public byte [] Payload { get; set; }
    [Required]
    public Child1 Child1 { get; set; }
    [Required]
    public Child2 Child2 { get; set; }
}

Note

Do not pay attention to the names of the classes and their properties; this is just a simple example.

Notice that for the Child2 class, we do not mark the Parent property as required, this is what allows us to use the SetNull cascade option. Also, we do not need to specify the cascade delete behavior for all the other relations, this is the default.

So, with this setup, what happens if we delete a Parent instance is this:

  • If the Children1 or Children2 collections have not been loaded, Entity Framework will only remove records from the Parent table

  • All loaded entities in the Child1 table that are related with the Parent instance (Children1 collection) are deleted with a single DELETE command

  • All loaded entities in the Child2 table that are related with Parent (Children2) have their Parent property set to NULL one by one

  • If any of the GrandChildren collections was loaded, the entries in the GrandChild table stored in it will be deleted one by one

Of course, if we want, we can disable cascade deletes altogether:

modelBuilder
    .Entity<Child1>()
    .HasMany(c => c.GrandChildren)
    .WithOne(c => c.Child1)
    .OnDelete(DeleteBehavior.Restrict);

However, in this case we must not forget to remove the dependent entities before removing the principal one.

lock icon
The rest of the page is locked
Previous PageNext Chapter
You have been reading a chapter from
Entity Framework Core Cookbook - Second Edition
Published in: Nov 2016Publisher: PacktISBN-13: 9781785883309

Author (1)

author image
Ricardo Peres

Ricardo Peres is a Portuguese developer, blogger, and book author and is currently a team leader at Dixons Carphone. He has over 20 years of experience in software development and his interests include distributed systems, architectures, design patterns, and .NET development. He won the Microsoft MVP award in 2015 and has held this title up to 2020. He also authored Entity Framework Core Cookbook – Second Edition and Mastering ASP.NET Core 2.0, and was a technical reviewer for Learning NHibernate 4 for Packt. He also contributed to Syncfusion's Succinctly collection, with titles on .NET development. Ricardo maintains a blog—Development With A Dot—where he writes about technical issues. You can catch up with him on Twitter at @rjperes75.
Read more about Ricardo Peres