使用复合主键的多个一对一关系

问题描述 投票:0回答:1

我正在尝试进行一项实验,从列表中始终有 2 个的一对多切换,并使用复合键具有 2x 一对一关系。 建模类:

public class Game
{
    [Key]
    public long Id {get; set;}
    public long HomeTeamId { get; set; }
    public long AwayTeamId { get; set; }
    public TeamGameStats HomeTeamStats { get; set; }
    public TeamGameStats AwayTeamStats { get; set; }
    //original code would be what you expect from a one-to-many relations:
    //public List<TeamGameStats> TeamStats{get;set;} = new();
}

所以可以把它想象成任何有主场和客场的体育比赛,但有时它会切换。 我将省略

Team
,因为它们不相关

public class TeamGameStats
{
   public Game Game { get; set; }
   //current working attempt
   [Key]
   [Column(Order = 1)]
   public long GameId { get; set; }
   [Key] //not a key in original code
   [Column(Order = 2)]
   public long TeamId { get; set; }
   public long Score { get; set; }
   public long Catches { get; set; }
}

我当前的尝试在

OnModelCreating
中配置失败,这是我不理解的地方,可以使用一些指导。我试图通过这个实验来更深入地了解 EFCore 并建立关系。我通常保持模型足够简单,不需要覆盖
OnModelCreating
,但我想尝试一些奇怪和更复杂的东西。

protected override void OnModelCreating(ModelBuilder builder)
{
   base.OnModelCreating(builder);
   builder.Entity<TeamGameStats>().HasKey(tgs => new { tgs.GameId, tgs.TeamId });

   builder.Entity<Game>()
       .HasOne(g => g.HomeTeamStats)
       .WithOne(tgs => tgs.Game)
       .HasForeignKey<TeamGameStats>(nameof(TeamGameStats.GameId), nameof(TeamGameStats.TeamId) );

   //errors on this line
   builder.Entity<Game>()
       .HasOne(g => g.AwayTeamStats)
       .WithOne(tgs => tgs.Game)
       .HasForeignKey<TeamGameStats>(nameof(TeamGameStats.GameId), nameof(TeamGameStats.TeamId));
}

我得到的错误是:

System.InvalidOperationException: 'Cannot create a relationship between 'Game.AwayTeamStats' and 'TeamGameStats.Game' because a relationship already exists between 'Game.HomeTeamStats' and 'TeamGameStats.Game'. Navigations can only participate in a single relationship. 

所以我知道错误告诉我导航只能有 1 个设置。但我不明白为什么?我可以通过从 Game->TeamGameStats 建立一个关系,然后从 TeamGameStates->Game 建立另一个关系(看起来又傻又坏)来破解它吗? 对我来说这个想法应该是可能的,虽然它不是最好的,但我不明白为什么如果我使用复合键就不能有多个一对一? 我使用 Fluent 方法尝试了几种不同的方法,但上面的方法似乎是最远的,无一例外地说一切都是错误的。

考虑以下示例:

  • 熊队:Id = 5
  • 鲨鱼队:Id = 12

熊队与鲨鱼队交手3次,主客场轮流进行。

  • 第 1 场比赛:Id = 35,HomeTeamId = 5(熊队),AwayTeamId = 12(鲨鱼队)
  • 因此 HomeTeamStats = (35,5) 和 AwayTeamStats = (35,12) 的 PK
  • 第 2 场比赛:Id = 67,HomeTeamId = 12(鲨鱼),AwayTeamId = 5(熊)
  • 因此 HomeTeamStats = (67,12) 和 AwayTeamStats = (67,5) 的 PK
  • 第 3 场比赛:Id = 89,HomeTeamId = 5(熊队),AwayTeamId = 12(鲨鱼队)
  • 因此 HomeTeamStats = (89,5) 和 AwayTeamStats = (89,12) 的 PK

我想要的一个用例是更轻松地查询 TeamStats 表,而无需进行一些逻辑检查。目前,我要么将 Game.HomeTeamId 与列表进行比较,要么使用 TeamStats 中的布尔值

IsHomeTeam
来查找主队和客队。 如果我需要更新现有数据,它也会有所帮助。 还要看看是否可能,因为在断点中读取班级信息比查看列表并查找指示是主队还是客队的属性更容易。

当前存在的所有内容(具有列表和一对多关系)都可以正常工作。我正在做一些重构,并想我会尝试改变它来学习。

c# .net-core entity-framework-core ef-code-first
1个回答
0
投票

尝试在 TeamGameStats 端表达 FK 时,您有两种关系,但请注意 FK 定义是相同的。这些其实就是这张桌子的PK。解决方案是告诉 EF“FK”位于可以区分两者的一侧,即在 Game 上,带有 HomeTeamId 和 AwayTeamid:

builder.Entity<Game>()
   .HasOne(g => g.HomeTeamStats)
   .WithOne(tgs => tgs.Game)
   .HasForeignKey<Game>(g => new {g.GameId, g.HomeTeamId} );

builder.Entity<Game>()
   .HasOne(g => g.AwayTeamStats)
   .WithOne(tgs => tgs.Game)
   .HasForeignKey<Game>(g => new {g.GameId, g.AwayTeamId} );
© www.soinside.com 2019 - 2024. All rights reserved.