ASP.NET MVC6中的实体框架7多个外键到同一个表

时间:2016-01-30 05:03:22

标签: asp.net-mvc entity-framework entity-framework-core

嗨我有同样的问题,这里有一个老帖子,解决方案提供的对我来说在MVC 6中使用EF7很简单

public class Match
{
    [Key]
    public int MatchId { get; set; }

    public DateTime playday { get; set; }
    public float HomePoints { get; set; }
    public float GuestPoints { get; set; }

    public int HomeTeamId { get; set; }
    public int GuestTeamId { get; set; }

    [ForeignKey("HomeTeamId")]
    [InverseProperty("HomeMatches")]
    public virtual Team HomeTeam { get; set; }

    [ForeignKey("GuestTeamId")]
    [InverseProperty("AwayMatches")]
    public virtual Team GuestTeam { get; set; }

}

public class Team
{
    public int TeamId { get; set; }
    public String name { get; set; }

    public virtual ICollection<Match> HomeMatches { get; set; }
    public virtual ICollection<Match> AwayMatches { get; set; }
}

这是我找到的最佳方式,因为我可以添加新的迁移,一切正常,但是当我更新数据库时,我收到这样的错误

  

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

2 个答案:

答案 0 :(得分:4)

我在准备the answer时详细分析了问题,我建议你解决问题的两个方法。

由于类Match

中有两个属性,因此存在问题
public int HomeTeamId { get; set; }
public int GuestTeamId { get; set; }

以及将生成的外键HomeTeamIdGuestTeamId。 EF7使用ON DELETE CASCADE生成外键,不能将其用作一个外键。实体框架(RC1)的当前实现没有注释属性,您可以使用它来更改行为。

问题的第一个解决方案是使用像

这样的可空属性
public int? HomeTeamId { get; set; }
public int? GuestTeamId { get; set; }

public int HomeTeamId { get; set; }
public int? GuestTeamId { get; set; }

最多一个属性应该是不可为空的。结果问题将得到解决,但是会有一个小缺点,这对某些情况可能并不重要。 nullable属性的数据库表中的字段在列定义中没有NOT NULL属性。

如果确实需要同时保持HomeTeamIdGuestTeamId不可空,那么您可以通过修改上下文类(继承自DbContext)来解决问题,其中类MatchTeam可以使用。

您已经在下面定义了一些上下文类

public class MyDBContext : DbContext
{
    DbSet<Team> Teams { get; set; }
    DbSet<Match> Matches { get; set; }
}

要解决描述问题,您可以在明确设置

的类中添加受保护的OnModelCreating
public class MyDBContext : DbContext
{
    protected override void OnModelCreating(ModelBuilder modelbuilder)
    {
        base.OnModelCreating(modelbuilder);

        modelbuilder.Entity(typeof (Match))
            .HasOne(typeof (Team), "GuestTeam")
            .WithMany()
            .HasForeignKey("GuestTeamId")
            .OnDelete(DeleteBehavior.Restrict); // no ON DELETE

        modelbuilder.Entity(typeof (Match))
            .HasOne(typeof (Team), "HomeTeam")
            .WithMany()
            .HasForeignKey("GuestTeamId")
            .OnDelete(DeleteBehavior.Cascade); // set ON DELETE CASCADE
    }

    DbSet<Team> Teams { get; set; }
    DbSet<Match> Matches { get; set; }
}

您可以在两个外键上使用原因DeleteBehavior.Restrict(而不是在一个外键上使用DeleteBehavior.Cascade)。重要的是要注意,最后一种方法允许将HomeTeamIdGuestTeamId与数据库中的相应字段一样保持为不可为空。

有关其他信息,请参阅the documentation

答案 1 :(得分:0)

如果您愿意,可以使用级联删除手动在数据库上创建此FK,并检查您为什么要使用此循环级联循环。要了解我们需要其他表或涉及的数据库结构。请检查您的数据库(例如图表),绘制您的连接表,您的FK以及每个研究您计划的操作(级联删除,无操作,......)及其方向。当你试图错误地插入那个FK时,你会找到一个循环。

下一阶段是要了解您是否需要它,是否是一个不需要的数据库设计?或者只是你不需要这个FK行动?在这种情况下,您可以在各种级别禁止它:在模型,设置,...其他帖子或EF指南。