5

Entity Framework Core 5 introduced Many-to-Many relationships without directly creating join tables. This is great and makes coding faster, but I have experienced some challenges.

When working on relationships between two classes (student/teacher) who both inherits the same class (person) I get an error when updating the database after a migration.

Introducing FOREIGN KEY constraint 'FK_Student_TeacherId' on table 'StudentTeacher' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints. Could not create constraint or index. See previous errors.

Many-to-Many relationships seems to work seemlessly when not using inherited class.

public class Person
{
    public int Id { get; set; }
    public string SchoolName { get; set; }
    public int MyProperty { get; set; }
}

public class Teacher : Person
{
    public ICollection<Student> Students { get; set; }
}

public class Student : Person
{
    public ICollection<Teacher> Teachers { get; set; }
}

public class PersonConfiguration : IEntityTypeConfiguration<Person>
{
    public void Configure(EntityTypeBuilder<Person> builder)
    {
        builder.ToTable("Persons").HasKey(k => k.Id);
    }
}

public class TeacherConfiguration : IEntityTypeConfiguration<Teacher>
{
    public void Configure(EntityTypeBuilder<Teacher> builder)
    {
        builder.ToTable("Persons");
        builder.HasMany(p => p.Students).WithMany(t => t.Teachers);
    }
}

public class StudentConfiguration : IEntityTypeConfiguration<Student>
{
    public void Configure(EntityTypeBuilder<Student> builder)
    {
        builder.ToTable("Persons");
    }
}

I can manually fix the issue by changing onDelete for either student or teacher to ReferentialAction.Restrict

But I dont find this solution good as it leaves orphant rows in the Join Table.

Lindstrøm
  • 396
  • 2
  • 16

1 Answers1

5

Apparently, according to this many-to-many-on-delete, there is no good candidate for EF to select for restriction. So, you have to manually define the relationship when configuring:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Teacher>()
        .HasMany(t => t.Students)
        .WithMany(s => s.Teachers)
        .UsingEntity<Dictionary<string, object>>(
            "Tutelage",
            x => x.HasOne<Student>().WithMany().OnDelete(DeleteBehavior.Cascade),
            x => x.HasOne<Teacher>().WithMany().OnDelete(DeleteBehavior.Restrict)
        );
}
Ryan Naccarato
  • 674
  • 8
  • 12
  • 1
    Just a notice for curious readers. Today I had to do this again, but messed up in the order of of One-To-Many relationships - i does matter. :-) – Lindstrøm Mar 14 '22 at 09:07