簡體   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