实体框架代码第一个到多个级联错误

时间:2016-08-23 10:28:56

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

我的Asp.Net Mvc项目中有4个模型类。我正在使用Entity Framework Code First(自动迁移)。我在运行命令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; }

}

2 个答案:

答案 0 :(得分:1)

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

在您的情况下,如评论所述,您的用户模型与Tickets和TicketMessages具有一对多的关系,您的Ticket模型与TicketMessages具有一对多的关系。这意味着删除用户记录将删除与该UserID关联的所有TicketMessages和Tickets,除了可能(对于系统)具有该UserID的TicketMessage也使用未被UserID级联删除的TickedID 。因此错误是因为它破坏了外键关系。

有两种解决方案:

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>();
}

这两行分别删除了One to Many和Many to Many级联删除约定。您可以根据需要将它们保留或移除。

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

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

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

编辑 - 我发现您可能希望TicketMessages和User之间的关系表示当该人不是创建故障单的同一用户时谁编写了该消息。如果是这种情况,我仍然建议删除与User表的关系,而是使用一个字段,您可以在该字段中填充您在创建Ticket Message时与其关联的任何用户信息,例如:名称。或者,您仍然可以拥有UserID字段但没有导航属性,删除外键关系。然后,在检索信息时,可以通过将此UserID与User表中的UserID相匹配来调用用户信息。

答案 1 :(得分:1)

谢谢你回答Rob! 我用以下代码解决了这个问题。

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