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

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

我想创建团队和比赛之间的关系:

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个外键到同一个表?

204395 次浏览

试试这个:

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引用。Match映射没有级联删除,因为它在这些自引用的多对多中不起作用。

你也可以试试这个:

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列允许null时,您就打破了循环。或者我们只是在欺骗EF模式生成器。

在我的情况下,这个简单的修改解决了问题。

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

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

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

这是因为级联删除默认启用。问题是,当您对实体调用delete时,它也会删除每个f键引用的实体。你不应该让'required'值为空来解决这个问题。一个更好的选择是删除EF Code First的Cascade删除约定:

modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();

在映射/配置时,显式地指示何时对每个子节点执行级联删除可能更安全。的实体。

我知道这是一个几年前的帖子,你可以用上面的解决方案解决你的问题。然而,我只是想建议那些仍然需要使用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: https://msdn.microsoft.com/en-us/data/jj591583?f=255&MSPPError=-2147217396#Relationships上阅读更多关于InverseProperty的信息

InverseProperty在EF核心使解决方案简单和干净。

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