Search code examples
c#asp.net-coreentity-framework-coreasp.net-core-mvc

Multiple cascading paths Entity Framework Core


I am facing an error when trying to set up foreign key constraints using Entity Framework Core:

Introducing FOREIGN KEY constraint 'FK_Comments_Reviews_ReviewId' on table 'Comments' 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.

Here are my models:

public class Comment
{
    [Key]
    public int Id { get; set; }
    
    [ForeignKey("User")]
    public string? UserId { get; set; }
    public virtual User User { get; set; }
    public DateTime CreatedDate { get; set; }
    
    [ForeignKey("Review")]
    public int ReviewId { get; set; }
    public Review Review { get; set; }
}

public class User : IdentityUser
{
    public string DisplayName { get; set; }

    public List<Comment> UserComments { get; set; } 
    public List<Like> UserLikes { get; set; } 
    public List<Review> UserReviews { get; set; }
    public List<Rating> UserRatings { get; set; }
}

public class Review
{
    public enum Group
    {
        Movies, Books, Games
    }

    [Key]
    public int Id { get; set; }
    
    [Required]
    public string ReviewName { get; set; }
    [Required]
    public string ArtName { get; set; }
    [Required]
    public Group ArtGroup { get; set; }
    
    public List<ReviewTag> ReviewTags { get; set; } 
    [Required]
    public string ReviewText { get; set; }  
    public string ImageUrl { get; set; } 

    [Range(0, 10)]
    public int Rating { get; set; }
    [ForeignKey("User")]
    public string UserId { get; set; }
    public virtual User User { get; set; }
    public DateTime CreatedDate { get; set; }

    public List<Comment> ReviewComments { get; set; }
    public List<Like> ReviewLikes { get; set; } 
}

//I also tried altering relationships

// User to Review relationship
modelBuilder.Entity<User>()
    .HasMany(u => u.UserReviews)
    .WithOne(r => r.User)
    .HasForeignKey(r => r.UserId)
    .OnDelete(DeleteBehavior.Cascade);

// User to Comment relationship
modelBuilder.Entity<User>()
    .HasMany(u => u.UserComments)
    .WithOne(c => c.User)
    .HasForeignKey(c => c.UserId)
    .OnDelete(DeleteBehavior.SetNull);

// Review to Comment relationship
modelBuilder.Entity<Review>()
    .HasMany(r => r.ReviewComments)
    .WithOne(c => c.Review)
    .HasForeignKey(c => c.ReviewId)
    .OnDelete(DeleteBehavior.Cascade);
}

Solution

  • I am facing an error when trying to set up foreign key constraints using Entity Framework Core:

    enter image description here

    Well, based on your error details and scenario,this is officially known issue. On top of this, We have two ways to handle this scenario therefore, the error:

    1. Change one or more of the relationships to not cascade delete.

    In this scenario, we could make the User relationship with UserDetail and Menu optional by giving it a nullable foreign key property: for instance we can do something like:

    .IsRequired(false);
    

    Note: You can check our official document for more details.

    2. Configure the database without one or more of these cascade deletes, then ensure all dependent entities are loaded so that EF Core can perform the cascading behavior.

    Considering this approach we can keep the User, Review relationship required and configured for cascade delete, but make this configuration only apply to tracked entities, not the database, thus, instead of this .OnDelete(DeleteBehavior.Cascade); replace with .OnDelete(DeleteBehavior.Restrict) .IsRequired(false);: So we can do somethng like below:

    modelBuilder.Entity<User>()
        .HasMany(u => u.UserReviews)
        .WithOne(r => r.User)
        .HasForeignKey(r => r.UserId)
        .OnDelete(DeleteBehavior.Cascade);
    
    // User to Comment relationship
    modelBuilder.Entity<User>()
        .HasMany(u => u.UserComments)
        .WithOne(c => c.User)
        .HasForeignKey(c => c.UserId)
        .OnDelete(DeleteBehavior.SetNull);
        .IsRequired(false);
    // Review to Comment relationship
    modelBuilder.Entity<Review>()
        .HasMany(r => r.ReviewComments)
        .WithOne(c => c.Review)
        .HasForeignKey(c => c.ReviewId)
        .OnDelete(DeleteBehavior.Restrict)
        .IsRequired(false);
    
    }
    
           
    

    Note: As you may know OnDelete(DeleteBehavior.ClientCascade) or ClientCascade allows the DBContext to delete entities even if there is a cyclic ref or LOCK on it. Please read the official guideline for more details here