在表“y”上引入外键约束“x”可能会导致循环或多个级联路径

mznpcxlj  于 2021-07-24  发布在  Java
关注(0)|答案(1)|浏览(294)

我知道关于这个错误消息有多个问题,但是我没有找到对我有帮助的东西。
我有这样的情况:
用户模型

public class User : IdentityUser<int>
{
    ......
    public virtual ICollection<Like> Likers { get; set; }
    public virtual ICollection<Like> Likees { get; set; }
    public virtual ICollection<Message> MessagesSent { get; set; }
    public virtual ICollection<Message> MessagesReceived { get; set; }
}

相似模型

public class Like
{
    public int LikerId { get; set; }
    public int LikeeId { get; set; }
    public virtual User Liker { get; set; }
    public virtual User Likee { get; set; }
}

消息模型

public class Message
{
    public int Id { get; set; }
    public int SenderId { get; set; }
    public virtual User Sender { get; set; }
    public int RecipientId { get; set; }
    public virtual User Recipient { get; set; }
    .....
}

在datacontext中

.....
builder.Entity<Like>()
    .HasKey(k => new { k.LikerId, k.LikeeId });

builder.Entity<Like>()
    .HasOne(u => u.Likee)
    .WithMany(u => u.Likers)
    .HasForeignKey(u => u.LikeeId)
    .OnDelete(DeleteBehavior.Cascade);

builder.Entity<Like>()
    .HasOne(u => u.Liker)
    .WithMany(u => u.Likees)
    .HasForeignKey(u => u.LikerId)
    .OnDelete(DeleteBehavior.Cascade);

builder.Entity<Message>()
    .HasOne(u => u.Sender)
    .WithMany(u => u.MessagesSent)
    .OnDelete(DeleteBehavior.Cascade);

builder.Entity<Message>()
    .HasOne(u => u.Recipient)
    .WithMany(u => u.MessagesReceived)
    .OnDelete(DeleteBehavior.Cascade);
.....

如果我有 .OnDelete(DeleteBehavior.Cascade); 我在尝试应用迁移时收到此错误消息: An error occured during migration Introducing FOREIGN KEY constraint 'FK_Likes_AspNetUsers_LikerId' on table 'Likes' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints. likes和messages表都有相同的错误。
解决办法是 .OnDelete(DeleteBehavior.Restrict); ,但是如果我想在应用程序中添加一个选项来删除某个用户,那么如果该用户喜欢某个人或者他发送了一条消息,我就不能这样做。首先,我需要手动删除他所有的喜欢和他的消息,然后再执行用户删除,这就是为什么我想这与级联删除自动。
此错误消息仅在我使用microsoft sql server时出现,它与sqlite一起工作。
编辑:作为一种解决方法,我选择了@lauxjpn所建议和更改的内容 .OnDelete(DeleteBehavior.Delete);.OnDelete(DeleteBehavior.Restrict); 并创建了一个db触发器,用于在删除父记录之前处理子记录(like和messages)的删除。
我测试了这个方法,它是有效的。首先我用一个新用户登录,喜欢某人并发送了一条消息,然后我删除了我的帐户。触发器处理其余的内容,从likes和messages中删除记录,然后从aspnetusers中删除用户。
这是触发器:

CREATE TRIGGER [DELETE_User]
   ON [dbo].[AspNetUsers]
   INSTEAD OF DELETE
AS 
BEGIN
 SET NOCOUNT ON;
 DELETE FROM [dbo].[Likes] WHERE LikerId IN (SELECT Id FROM DELETED)
 DELETE FROM [dbo].[Likes] WHERE LikeeId IN (SELECT Id FROM DELETED)
 DELETE FROM [dbo].[Messages] WHERE SenderId IN (SELECT Id FROM DELETED)
 DELETE FROM [dbo].[Messages] WHERE RecipientId IN (SELECT Id FROM DELETED)
 DELETE FROM [dbo].[AspNetUsers] WHERE Id IN (SELECT Id FROM DELETED)
END

编辑2:(也许其他人会需要这个)
也可以在迁移中添加触发器:

public partial class AddedInstedOfTrigger : Migration
{
    protected override void Up(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.Sql(@"
        CREATE OR ALTER TRIGGER [DELETE_User]
          ON [dbo].[AspNetUsers]
          INSTEAD OF DELETE
        AS
        BEGIN
          SET NOCOUNT ON;
          DELETE FROM [dbo].[Likes] WHERE LikerId IN (SELECT Id FROM DELETED)
          DELETE FROM [dbo].[Likes] WHERE LikeeId IN (SELECT Id FROM DELETED)
          DELETE FROM [dbo].[Messages] WHERE SenderId IN (SELECT Id FROM DELETED)
          DELETE FROM [dbo].[Messages] WHERE RecipientId IN (SELECT Id FROM DELETED)
          DELETE FROM [dbo].[AspNetUsers] WHERE Id IN (SELECT Id FROM DELETED)
        END");
    }

    protected override void Down(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.Sql(@"DROP TRIGGER [DELETE_User]");
    }
}
xurqigkl

xurqigkl1#

我最近在这里回答了同样的问题,所以:
你不需要丢掉任何外键。只是使用 OnDelete(DeleteBehavior) 明确指定需要哪种级联行为。
例如,以下情况将导致成功创建模型,但对于实际应用程序,您需要自行决定在何处以及如何中断级联:

builder.Entity<PhotoDevice>()
    .HasOne(bc => bc.Photo)
    .WithMany(b => b.PhotoDevices)
    .HasForeignKey(bc => bc.PhotoRef)
    .OnDelete(DeleteBehavior.Restrict); // <-- define cascading behavior

有关详细信息,请参见关系:级联删除和级联删除。
这不是ef核心,而是sql server限制(因此,sql server也会引发异常)。
下面是更多的资源,它们处理这个限制,并展示如何使用 INSTEAD OF 触发器,如果中断级联不是您可以接受的选项:
使用触发器解决sql server多级联路径问题
避免循环/多个更新路径的首选设计

相关问题