【问题标题】:Entity Framework Code First One To Many Cascade Error实体框架代码第一个一对多级联错误
【发布时间】:2016-08-23 10:28:55
【问题描述】:

我的 Asp.Net Mvc 项目中有 4 个模型类。我正在使用实体框架代码优先(自动迁移)。我在运行命令 update-database 时,出现以下错误;

在表“Tickets”上引入 FOREIGN KEY 约束“FK_dbo.Tickets_dbo.Users_CreatedUserId”可能会导致循环或多个级联路径。指定 ON DELETE NO ACTION 或 ON UPDATE NO ACTION,或修改其他 FOREIGN KEY 约束。 无法创建约束或索引。查看以前的错误。

有什么问题?

//Company Class
public class Company
{
    public int Id { get; set; }

    [Required, StringLength(100)]
    public string Title { get; set; }

    public virtual ICollection<User> Users { get; set; } = new HashSet<User>();
}

//User Class
public class User
{
    public int Id { get; set; }

    [Required, StringLength(50)]
    public string UserName { get; set; }

    [Required, StringLength(100)]
    public string FullName { get; set; }

    public int CompanyId { get; set; }
    public virtual Company Company { get; set; }
}

//Ticket Class
public class Ticket
{
    public int Id { get; set; }

    [Required]
    public DateTime CreationDate { get; set; }

    [Required, StringLength(100)]
    public string Title { get; set; }

    public bool IsClosed { get; set; }

    public int CreatedUserId { get; set; }
    public virtual User CreatedUser { get; set; }

    public virtual ICollection<TicketMessage> Messages { get; set; } = new HashSet<TicketMessage>();
}

//TicketMessage Class
public class TicketMessage
{
    public int Id { get; set; }

    public DateTime Date { get; set; }

    [Required]
    public string Message { get; set; }

    public int UserId { get; set; }
    public virtual User User { get; set; }

    public int TicketId { get; set; }
    public virtual Ticket Ticket { get; set; }

}

【问题讨论】:

  • 那是因为表Users有很多Tickets和很多TicketMessages,同时Ticket有很多TicketMessages
  • 是的,应该是这样的。因为,用户可以创建工单和工单消息。
  • 但是如果您删除一个特定的User,SQL 引擎不知道该怎么办。你应该按照字面意思做:指定是ON DELETE NO ACTION还是自定义delete trigger

标签: c# asp.net-mvc entity-framework ef-code-first code-first


【解决方案1】:

如果您删除一个模型(父模型)中的记录,它将删除子模型中与其相关的所有记录。在这种情况下,Users 模型是具有许多票证和票证消息的父模型,因此当您删除用户时,级联删除也会删除所有用户票证和票证消息。当模型具有导航属性时,EF 会自动分配一个外键。外键在模型之间创建关系,这意味着当“子”模型中的记录依赖于作为主键出现在您尝试删除的记录中的外键时,您无法删除一个模型中的记录。

在评论所述的情况下,您的 User 模型与门票和 TicketMessages 具有一对多的关系,您的 Ticket 模型与 TicketMessages 具有一对多的关系。这意味着删除用户记录将删除与该用户 ID 关联的所有 TicketMessage 和工单,除非(对于系统)具有该用户 ID 的 TicketMessage 也使用 TickedID,该用户 ID 不会被级联删除.因此出现错误,因为这会破坏外键关系。

有两种解决方案:

1 - 修改您的设计,您的 TicketMessages 模型已经通过 Ticket ID 与 Ticket 有关系,因此与 UserID 有关系,因为 UserID 与 Ticket 模型中的 Ticket ID 相关联。因此,您可以从 TicketMessage 模型中删除 UserID 关系,这不是必需的。然后您的问题将得到解决。在我看来,这确实是正确的解决方案,因为问题在于您的设计。

2 - 如果您出于某种原因我无法察觉想要维持这种关系,您可以使用以下方法完全禁用级联删除:

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
    modelBuilder.Conventions.Remove<ManyToManyCascadeDeleteConvention>();
}

这两行分别删除了一对多和多对多级联删除约定。您可以根据需要保留或移除它们。

您还可以使用以下方法删除单个级联删除关系:

modelBuilder.Entity<ParentEntity>()
.HasMany(p => p.Children)
.WithRequired()
.HasForeignKey(c => c.ParentEntityId)
.WillCascadeOnDelete(false);

我强烈推荐解决方案 1,从 TicketMessages 模型中删除 UserID,您的 TicketMessages 已经通过 TicketMessages 模型和 Ticket 模型之间的关系链接到一个 UserID。

edit - 我想到您可能希望 TicketMessages 和 User 之间的关系表示当该人不是创建票证的同一用户时谁写了该消息。如果是这种情况,我仍然建议删除与 User 表的关系,而是使用一个字段,您可以在该字段中填充您希望在创建票证消息时与它关联的任何用户信息,例如姓名。或者,您仍然可以有一个 UserID 字段但没有导航属性,从而删除外键关系。然后,当您检索信息时,可以通过将此 UserID 与 User 表中的一个匹配来调用用户信息。

【讨论】:

    【解决方案2】:

    感谢您的回答罗布! 我用下面的代码解决了这个问题。

     modelBuilder.Entity<User>()
              .HasMany(e => e.TicketMessages)
              .WithRequired(e => e.User)
              .WillCascadeOnDelete(false);
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-06-18
      • 2012-05-23
      • 1970-01-01
      • 2015-05-14
      相关资源
      最近更新 更多