繁体   English   中英

实体框架代码优先 - 来自同一个表的两个外键

[英]Entity Framework Code First - two Foreign Keys from same table

我刚开始使用 EF 代码,所以我完全是这个主题的初学者。

我想在 Teams 和 Matches 之间创建关系:

1 场比赛 = 2 支球队(主队、客队)和结果。

我认为创建这样一个模型很容易,所以我开始编码:

public class Team
{
    [Key]
    public int TeamId { get; set;} 
    public string Name { get; set; }

    public virtual ICollection<Match> Matches { get; set; }
}


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

    [ForeignKey("HomeTeam"), Column(Order = 0)]
    public int HomeTeamId { get; set; }
    [ForeignKey("GuestTeam"), Column(Order = 1)]
    public int GuestTeamId { get; set; }

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

    public virtual Team HomeTeam { get; set; }
    public virtual Team GuestTeam { get; set; }
}

我得到一个例外:

引用关系将导致不允许的循环引用。 [ 约束名称 = Match_GuestTeam ]

我怎样才能创建这样一个模型,同一个表有 2 个外键?

试试这个:

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

public class Match
{
    public int MatchId { get; set; }

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

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

    public virtual Team HomeTeam { get; set; }
    public virtual Team GuestTeam { get; set; }
}


public class Context : DbContext
{
    ...

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Match>()
                    .HasRequired(m => m.HomeTeam)
                    .WithMany(t => t.HomeMatches)
                    .HasForeignKey(m => m.HomeTeamId)
                    .WillCascadeOnDelete(false);

        modelBuilder.Entity<Match>()
                    .HasRequired(m => m.GuestTeam)
                    .WithMany(t => t.AwayMatches)
                    .HasForeignKey(m => m.GuestTeamId)
                    .WillCascadeOnDelete(false);
    }
}

主键按默认约定映射。 团队必须有两个比赛集合。 您不能拥有两个FK引用的单个集合。 匹配是在没有级联删除的情况下映射的,因为它在这些自引用多对多中不起作用。

也可以在navigation属性上指定ForeignKey()属性:

[ForeignKey("HomeTeamID")]
public virtual Team HomeTeam { get; set; }
[ForeignKey("GuestTeamID")]
public virtual Team GuestTeam { get; set; }

这样您就不需要向OnModelCreate方法添加任何代码

我知道这是一个几年的帖子,你可以解决上述解决方案的问题。 但是,我只想建议对仍然需要的人使用InverseProperty。 至少你不需要在OnModelCreating中改变任何东西。

以下代码未经测试。

public class Team
{
    [Key]
    public int TeamId { get; set;} 
    public string Name { get; set; }

    [InverseProperty("HomeTeam")]
    public virtual ICollection<Match> HomeMatches { get; set; }

    [InverseProperty("GuestTeam")]
    public virtual ICollection<Match> GuestMatches { get; set; }
}


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

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

    public virtual Team HomeTeam { get; set; }
    public virtual Team GuestTeam { get; set; }
}

您可以在MSDN上阅读有关InverseProperty的更多信息: https ://msdn.microsoft.com/en-us/data/jj591583?f = 255 MSPPError = -2147217396#Relationships

这是因为默认情况下启用Cascade Deletes。 问题是,当您在实体上调用删除时,它也会删除每个f键引用的实体。 您不应该使“必需”值可以为空来解决此问题。 更好的选择是删除EF Code First的Cascade删除约定:

modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>(); 

在映射/配置时,明确指示何时为每个子节点执行级联删除可能更安全。 实体。

你也可以试试这个:

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

    [ForeignKey("HomeTeam"), Column(Order = 0)]
    public int? HomeTeamId { get; set; }
    [ForeignKey("GuestTeam"), Column(Order = 1)]
    public int? GuestTeamId { get; set; }

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

    public virtual Team HomeTeam { get; set; }
    public virtual Team GuestTeam { get; set; }
}

当您使FK列允许NULLS时,您正在打破循环。 或者我们只是在欺骗EF模式生成器。

就我而言,这个简单的修改解决了这个问题。

EF Core中的InverseProperty使解决方案变得简单而干净。

InverseProperty

所以理想的解决方案是:

public class Team
{
    [Key]
    public int TeamId { get; set;} 
    public string Name { get; set; }

    [InverseProperty(nameof(Match.HomeTeam))]
    public ICollection<Match> HomeMatches{ get; set; }

    [InverseProperty(nameof(Match.GuestTeam))]
    public ICollection<Match> AwayMatches{ get; set; }
}


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

    [ForeignKey(nameof(HomeTeam)), Column(Order = 0)]
    public int HomeTeamId { get; set; }
    [ForeignKey(nameof(GuestTeam)), Column(Order = 1)]
    public int GuestTeamId { get; set; }

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

    public Team HomeTeam { get; set; }
    public Team GuestTeam { get; set; }
}

我知道这是一个很老的问题,但在 2021 年来到这里,下面的 EF Core > 3 解决方案对我有用。

  1. 确保外键可以为空

  2. 指定删除时的默认行为

    public class Match { public int? HomeTeamId { get; set; } public int? GuestTeamId { get; set; } public float HomePoints { get; set; } public float GuestPoints { get; set; } public DateTime Date { get; set; } public Team HomeTeam { get; set; } public Team GuestTeam { get; set; } } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Match>() .HasRequired(m => m.HomeTeam) .WithMany(t => t.HomeMatches) .HasForeignKey(m => m.HomeTeamId) .OnDelete(DeleteBehavior.ClientSetNull); modelBuilder.Entity<Match>() .HasRequired(m => m.GuestTeam) .WithMany(t => t.AwayMatches) .HasForeignKey(m => m.GuestTeamId) .OnDelete(DeleteBehavior.ClientSetNull); }

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM