Recently, I have been working on a project in which I need to integrate EF Core. I'm trying to implement a chat application, but when I attempt to run the migration command, I encounter the following error.
Introducing FOREIGN KEY constraint 'FK_UserChatHistory_Users_SenderUserId' on table 'UserChatHistory' 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.
using Microsoft.EntityFrameworkCore;
using System.ComponentModel.DataAnnotations.Schema;
using System.ComponentModel.DataAnnotations;
namespace AspCoreMvcSingnalR.DatabaseEntity
{
public class ChatDbContext : DbContext
{
public DbSet<User> Users { get; set; }
public DbSet<UserChatHistory> UserChatHistory { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer("Data Source=DESKTOP-MFLCOI2;Initial Catalog=ChatDb;User ID=sa;Password=adk@1234;Encrypt=false;");
}
}
public class User
{
public Guid UserId { get; set; }
public string FullName { get; set; }
public string Email { get; set; }
public string Password { get; set; }
}
[Table("UserChatHistory", Schema = "dbo")]
public class UserChatHistory
{
public Guid Id { get; set; }
public virtual User SenderUser { get; set; }
public Guid SenderUserId { get; set; }
public virtual User ReceiverUser { get; set; }
public Guid ReceiverUserId { get; set; }
public string Message { get; set; }
public DateTime CreatedAt { get; set; }
}
}
I've been grappling with this issue for a while and haven't been able to figure out what's happening. However, after a struggle, I've managed to find a generic solution to this error. Let's discuss various available solutions to this error in detail.
This means that when a related record is deleted or updated, no action will be taken on the dependent records. You can specify this behavior for the foreign key constraint causing the issue.
Open your migration file and review the constraints. Change the onDelete ReferentialAction from Cascade to NoAction.
constraints: table =>
{
table.PrimaryKey("PK_UserChatHistory", x => x.Id);
table.ForeignKey(
name: "FK_UserChatHistory_Users_ReceiverUserId",
column: x => x.ReceiverUserId,
principalTable: "Users",
principalColumn: "UserId",
onDelete: ReferentialAction.NoAction);
table.ForeignKey(
name: "FK_UserChatHistory_Users_SenderUserId",
column: x => x.SenderUserId,
principalTable: "Users",
principalColumn: "UserId",
onDelete: ReferentialAction.NoAction);
});
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
if (modelBuilder == null)
throw new ArgumentNullException("modelBuilder");
foreach (var entityType in modelBuilder.Model.GetEntityTypes())
{
entityType.SetTableName(entityType.DisplayName());
entityType.GetForeignKeys()
.Where(fk => !fk.IsOwnership && fk.DeleteBehavior == DeleteBehavior.Cascade)
.ToList()
.ForEach(fk => fk.DeleteBehavior = DeleteBehavior.Restrict);
}
base.OnModelCreating(modelBuilder);
}
Final DbContext Code:
namespace AspCoreMvcSingnalR.DatabaseEntity
{
public class ChatDbContext : DbContext
{
public DbSet<User> Users { get; set; }
public DbSet<UserChatHistory> UserChatHistory { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer("Data Source=DESKTOP-MFLCOI2;Initial Catalog=ChatDb;User ID=sa;Password=adk@1234;Encrypt=false;");
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
if (modelBuilder == null)
throw new ArgumentNullException("modelBuilder");
foreach (var entityType in modelBuilder.Model.GetEntityTypes())
{
entityType.SetTableName(entityType.DisplayName());
entityType.GetForeignKeys()
.Where(fk => !fk.IsOwnership && fk.DeleteBehavior == DeleteBehavior.Cascade)
.ToList()
.ForEach(fk => fk.DeleteBehavior = DeleteBehavior.Restrict);
}
base.OnModelCreating(modelBuilder);
}
}
public class User
{
public Guid UserId { get; set; }
public string FullName { get; set; }
public string Email { get; set; }
public string Password { get; set; }
}
[Table("UserChatHistory", Schema = "dbo")]
public class UserChatHistory
{
public Guid Id { get; set; }
public virtual User SenderUser { get; set; }
public Guid SenderUserId { get; set; }
public virtual User ReceiverUser { get; set; }
public Guid ReceiverUserId { get; set; }
public string Message { get; set; }
public DateTime CreatedAt { get; set; }
}
This method customizes the behavior of foreign key constraints in the database context by setting the delete behavior to Restrict for foreign keys involved in relationships where cascading deletes could potentially cause issues.