外键约束问题

本文关键字:问题 约束 | 更新日期: 2023-09-27 18:27:03

我已经对EF代码优先数据库进行了一些更改,当我尝试更新它时,我现在得到了以下错误:

在"SupportTicketMessages"表上引入FOREIGN KEY约束"FK_dbo.SupportTicketMessages_dbo.SupportTickets_Ticket_Id"可能会导致循环或多个级联路径。指定ON DELETE NO ACTION或ON UPDATE NO ACTION,或修改其他FOREIGN KEY约束

以下是我的实体:

支持票证:

public class SupportTicket
    {
        [Key]
        [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
        public int Id { get; set; }
        [Required]
        public string Title { get; set; }
        [Required]
        public string Text { get; set; }
        [Required]
        public TicketUrgency Urgency { get; set; }
        [Required]
        public TicketStatus Status { get; set; }
        [Required]
        public virtual UserProfile Owner { get; set; }
        [Required]
        public DateTime Date { get; set; }
}

SupportTicketMessage:

public class SupportTicketMessage
    {
        [Key]
        [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
        public int Id { get; set; }
        [Required]
        public virtual UserProfile Author { get; set; }
        [Required]
        public string Text { get; set; }
        [Required]
        public DateTime Date { get; set; }
        [Required]
        public virtual SupportTicket Ticket { get; set; }
        [Required]
        public int MessageNumber { get; set; }
    }

这里有什么问题?我不知道出了什么问题。

外键约束问题

问题是两个实体中必需的UserProfile

UserProfileSupportTicketSupportTicketMessage:之间需要三个一对多关系

  1. 一个UserProfile-多个SupportTicket
  2. 一个UserProfile-多个SupportTicketMessage
  3. 一个SupportTicket-多个SupportTicketMessages

(左侧是主体(具有关系的主键),右侧是从属(具有关系外键)。)

对于所需的一对多关系,EF默认情况下向数据库添加级联删除,即,如果删除了上面左侧的实体,则右侧的所有依赖实体都应自动删除。

如果您现在要删除一个UserProfile,那么到SupportTicketMessage表将有两个级联删除路径,即:

  • UserProfile->SupportTicket->SupportTicketMessage(因为关系1和3)
  • UserProfile->SupportTicketMessage(因为关系2)

这在SQL Server中是不允许的,以及异常的原因。

为了解决此问题,您必须"断开"至少一个关系的级联删除路径。您可以通过使关系可选(即:删除导航属性上的一个[Required]属性)或显式禁用级联删除来实现这一点。我会选择后一个选项,因为将关系从必需更改为可选有点像更改业务规则。我会禁用关系1和2的级联删除,因为删除UserProfile可能不应该删除所有SupportTicketSupportTicketMessage,而是应该将票证和消息分配给某个"匿名"默认用户,这样当用户想要离开系统时,票证历史记录就不会丢失。

必须使用Fluent API禁用级联删除:

modelBuilder.Entity<SupportTicket>()
    .HasRequired(s => s.Owner)
    .WithMany()
    .WillCascadeOnDelete(false);
modelBuilder.Entity<SupportTicketMessage>()
    .HasRequired(s => s.Author)
    .WithMany()
    .WillCascadeOnDelete(false);

(如果您在UserProfile中有引用SupportTicket s和SupportTicketMessage s的导航集合,则必须使用带有lambda参数的WithMany调用,如WithMany(u => u.SupportTickets)WithMany(u => u.SupportTicketMessages)。)