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 日更新
PlayerId
在 Player
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
是必需的。我的猜测是:
- 宇宙有
List<Player>
。但是在Fluent API中并没有规定Player必须附加到一个服务器上。创建第一个 ServerID
以维持关系。
- 当 EF 稍后查看
Player
时。 Player
到 Universe
的关系进来了。第二个 ServerId1
然后用来保持 1 对 1 的关系。
我正在使用 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 日更新
PlayerId
在 Player
class 而不是 Universe
class。那只是复制时的错误。
.WithMany(u => u.Players)
生成与评论中建议的 .WithMany()
相同的结果。迁移文件也有空白 Up
/Down
。
EF Core 假定外键的格式为
请尝试以下操作:
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 建议的
观察到 ServerId
可以为空,ServerId1
是必需的。我的猜测是:
- 宇宙有
List<Player>
。但是在Fluent API中并没有规定Player必须附加到一个服务器上。创建第一个ServerID
以维持关系。 - 当 EF 稍后查看
Player
时。Player
到Universe
的关系进来了。第二个ServerId1
然后用来保持 1 对 1 的关系。