Entity Framework 复合键映射

Entity Framework Composite Keys Mapping

我的数据库中有 2 个 table:

游戏:

public class Game
{
    [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    [ForeignKey("WinningSlot"), Column(Order = 0)]
    public int Id { get; set; }
    public int numer { get; set; }
    public string md5 { get; set; }
    public string secret { get; set; }
    public GameStatus Status { get; set; }
    public int SlotsNumber { get; set; }
    public DateTime CreationTime { get; set; }
    public DateTime? EndDime { get; set; }
    public DateTime? ForceEndTime { get; set; }
    [ForeignKey("WinningSlot"), Column(Order = 1)]
    public int WinningSlotId { get; set; 
    public GameSlot WinningSlot { get; set; }
    public double SlotPrice { get; set; }
    public List<GameSlot> Slots { get; set; }
}

游戏机:

public class GameSlot
{
    [Column(Order = 0), Key]
    public int GameId { get; set; }
    [ForeignKey("GameId")]
    public Game Game { get; set; }
    [Column(Order = 1), Key]
    public int Slot { get; set; }
    public int? PlayerId { get; set; }
    [ForeignKey("PlayerId")]
    public WotUser Player { get; set; }
    public string SecretData { get; set; }
}

如您所见,GameSlot table 的 PK 是复合的,由 GameID(也是 Game 的 FK)和 Slot 组成(这是插槽号)。一切正常,直到我尝试将 WinningSlot 属性 添加到我的 Game table。它在数据库更新时在 GameSlot table 中为其生成另一个 Game_Id 列。

我所有试图弄乱流利 api 的尝试都失败了。你能帮我建立一个正常的关系吗? winningslot 列是可选的,可以为空。谢谢!

更新: 使用 FluentApi 解决的问题:

 modelBuilder.Entity<Game>()
                .HasOptional(g => g.WinningSlot)
                .WithMany()
                .HasForeignKey(g => new { g.Id, g.WinningSlotId });

但这是另一个: 当我尝试添加这样的插槽时(示例代码),它工作正常:

var game = db.Games.Find(1);
game.WinningSlotId = 1;
db.SaveChanges();

但是如果我想改为设置插槽:

 var game = db.Games.Find(1);
 var gameslot = db.Slots.Find(game.Id, 1);
 game.WinningSlot = gameslot;
 db.SaveChanges();

它会抛出异常告诉我我不能修改游戏的 Id 属性。如果 gameslot 的 gameID = game.Id?

有什么问题

你可以试试这个:

modelBuilder.Entity<GameSlot>()
  .HasKey(g => new { g.GameID, g.Slot })
  .HasRequired(g => g.Game)
  .WithMany(g => g.GameSlot);

编辑:我发现了为什么要创建索引,您需要将导航属性设为虚拟。因此,您不需要我上面写的流利的 api,但我个人认为流利的映射比使用数据注释更清晰,就像您所做的那样,不会使您的模型与属性混淆。

public class Game
{
public Game()
{
    Slots = new HashSet<GameSlot>();
}

[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[ForeignKey("WinningSlot"), Column(Order = 0)]
public int Id { get; set; }
public int numer { get; set; }
public string md5 { get; set; }
public string secret { get; set; }
public GameStatus Status { get; set; }
public int SlotsNumber { get; set; }
public DateTime CreationTime { get; set; }
public DateTime? EndDime { get; set; }
public DateTime? ForceEndTime { get; set; }
[ForeignKey("WinningSlot"), Column(Order = 1)]
public int WinningSlotId { get; set;     
public double SlotPrice { get; set; }

public virtual GameSlot WinningSlot { get; set; }
public virtual ICollection<GameSlot> Slots { get; set; }
}

(将接口与哈希集一起用于您的导航集合属性更为常见。)

此外,如果您希望 Game 可以从 GameSlot 访问,或者通过 lazy/eager 加载,您将必须添加导航 属性 到 Game 也是,但在旁注中。

关于你遇到的第二个错误,Game.Id既是Game的主键又是GameSlot的外键。如果您尝试指定另一个 winningslot 进行游戏,那么您也在尝试更改 GameSlot 的 ID。