Entity Framework 不使用我的外键而是生成一个新列

Entity Framework not using my foreign key but generating a new column instead

我正在使用 Fluent API 和代码优先方法来定义架构。我想知道为什么迁移会生成一个新列而不是使用定义的外键。因此,DbSet 没有 link 正确。

我正在关注这个例子:https://docs.microsoft.com/en-us/ef/core/modeling/relationships

关系是这样的:服务器玩家(父子)

Player.cs

public class Player
{
    public int Id { get; set; }
    public int PlayerId { get; set; }
    public string Name { get; set; }

    public int ServerId { get; set; }
    public Universe Server { get; set; }
}

Universe.cs(服务器)

public class Universe
{
        [DatabaseGenerated(DatabaseGeneratedOption.None)]
        public int Id { get; set; }
        public string Name { get; set; }
        public string Domain { get; set; }

        public List<Planet> Players { get; set; } = new List<Player>();
}

PlayerConfiguration.cs(流利API)

public class PlayerConfiguration : IEntityTypeConfiguration<Player>
{
    public void Configure(EntityTypeBuilder<Player> builder)
    {
        builder.ToTable("Player")
            .HasAlternateKey(e => new { e.PlayerId, e.ServerId });
        builder.HasOne<Universe>()
            .WithMany()
            .HasForeignKey(e => e.ServerId);
    }
}

EF生成的数据库脚本:

CREATE TABLE [dbo].[Player] 
(
    [Id]         INT            IDENTITY (1, 1) NOT NULL,
    [Name]       NVARCHAR (MAX) NULL,
    [ServerId]   INT            NOT NULL,
    [ServerId1]  INT            NULL,

    CONSTRAINT [PK_Player] PRIMARY KEY CLUSTERED ([Id] ASC),

    CONSTRAINT [AK_Player_PlayerId_ServerId] 
        UNIQUE NONCLUSTERED ([PlayerId] ASC, [ServerId] ASC),
    CONSTRAINT [FK_Player_Universe_ServerId1] 
        FOREIGN KEY ([ServerId1]) REFERENCES [dbo].[Universe] ([Id])
);

GO

CREATE NONCLUSTERED INDEX [IX_Player_ServerId]
ON [dbo].[Player]([ServerId] ASC);
GO

CREATE NONCLUSTERED INDEX [IX_Player_ServerId1]
ON [dbo].[Player]([ServerId1] ASC);

所以当我调用 context.Universes.Players.Add(...some players...)context.SaveChanges() 时。 ServerIds 将保存到 ServerId1 并在 ServerId 列中保持默认值。当我尝试从数据库中查询数据时,找不到任何玩家。

当我输入问题时,我意识到 AK 使用 ServerID 作为连接,而 FK 使用 ServerId1。但是,我需要设置一个复合唯一键来标识每个玩家在服务器中只有一个 ID。 Id 列是系统生成的。而 PlayerId 是从不同的服务器捕获的。 PlayerId 可以跨服务器复制。

我将系统设计为生成一个新的 Id,因为它经常被 link 编辑到系统用于其他目的。每次调用都会link一个复合键浪费时间

我从 THIS ANSWER 那里得到了定义唯一键的示例,这表明我可以使用备用键。这实际上不是真的吗?或者我实际上没有正确定义 FK?


2018 年 1 月 12 日更新
PlayerIdPlayer class 而不是 Universe class。那只是复制时的错误。

.WithMany(u => u.Players) 生成与评论中建议的 .WithMany() 相同的结果。迁移文件也有空白 Up/Down

EF Core 假定外键的格式为 Id。
请尝试以下操作:

public class Player
{
    public int Id { get; set; } 
    public string Name { get; set; }

    public int UniverseId { get; set; }
    public Universe Universe { get; set; }
}

public class Universe
{
    [DatabaseGenerated(DatabaseGeneratedOption.None)]
    public int Id { get; set; }
    public int PlayerId { get; set; } // I think you can do without this.
    public string Name { get; set; }
    public string Domain { get; set; }

    public List<Planet> Planets { get; set; } = new List<Planet>();
}

感谢社区提供的所有评论和回答。
最终目标的答案实际上比问题要低得多。

由于延迟加载,Players 列表没有拉起。
这是通过在加载脚本中添加 context.Universes.Include(u => u.Players) 来解决的。


然而,有趣的发现是也生成了多个 FK。我将在下面深入介绍。

更新 1 - 14/01/2018
Fluent API 的正确调用方式是

builder.HasOne(<b>e => e.Server</b>).WithMany(u => u.Players).HasForeignKey(e => e.ServerId)

正如 Ivan 在评论中所建议的

.HasOne<Universe>() is telling EF that the relationship does not have reference navigation property in Player class, hence when it discovers Player.Server navigation property, it creates another one-to-many relationship.


我想知道这是 Fluent API 的一个错误。 建议的数据注释方法通过添加

完美地工作
//To Universe Class
[InverseProperty("Server")]
public List<Player> Players { get; set; } = new List<Player>();

//To Player Class
public int ServerId { get;set; }
[ForeignKey("ServerId")]
public Universe Server { get; set; }

事实证明,如果我使用 Fluent API

builder.HasOne<Universe>()
    .WithMany(u => u.Players)
    .HasForeignKey(e => e.ServerId);

它将生成两次关系。 我想知道 Fluent API 是否需要 InverseProperty,因为 Official Documentation 没有这么说。

我也尝试了@rufus-lobo 建议的 说导航名称应该由 EF 保留。但是在使用 Fluent API.

时它仍然会生成两次 FK 列

观察到 ServerId 可以为空,ServerId1 是必需的。我的猜测是:

  1. 宇宙有List<Player>。但是在Fluent API中并没有规定Player必须附加到一个服务器上。创建第一个 ServerID 以维持关系。
  2. 当 EF 稍后查看 Player 时。 PlayerUniverse 的关系进来了。第二个 ServerId1 然后用来保持 1 对 1 的关系。