是否可以使用实体框架代码优先数据批注创建双向外键关系
本文关键字:创建 关系 数据 实体 可以使 框架 代码 是否 | 更新日期: 2023-09-27 18:30:59
我正在使用 EF 6 Code First 并试图找出使用数据注释配置关系的最佳方法,但由于某种原因,EF 正在向架构添加我不想要的额外列。
我有两个实体:Ship
和Voyage
。一艘船可以有很多航程;一次航行属于一艘船,也只属于一艘船。所以我从这个开始(出于 SO 目的进行了简化):
public class Ship
{
public int Id { get; set; }
public virtual ICollection<Voyage> Voyages { get; set; }
}
public class Voyage
{
public int Id { get; set; }
public int ShipId { get; set; }
public virtual Ship Ship { get; set; }
public DateTimeOffset Started { get; set; }
}
这将在数据库中创建两个表,如下所示:
CREATE TABLE [dbo].[Ship] (
[Id] INT IDENTITY (1, 1) NOT NULL,
CONSTRAINT [PK_dbo.Ship] PRIMARY KEY CLUSTERED ([Id] ASC)
);
CREATE TABLE [dbo].[Voyage] (
[Id] INT IDENTITY (1, 1) NOT NULL,
[ShipId] INT NOT NULL,
[Started] DATETIMEOFFSET (7) NOT NULL,
CONSTRAINT [PK_dbo.Voyage] PRIMARY KEY CLUSTERED ([Id] ASC),
CONSTRAINT [FK_dbo.Voyage_dbo.Ship_ShipId] FOREIGN KEY ([ShipId]) REFERENCES [dbo].[Ship] ([Id]) ON DELETE CASCADE
);
目前为止,一切都好。当我想查询此数据时,问题就来了。我需要获得船只清单,但对于每艘船,我也想要最近的航行。我看不到一种编写 LINQ 查询的方法可以一次性完成此操作,即使我能够使用 SQL 编写这样的查询。
此时我的选择似乎是:
- 装载所有
- 船只,并为每艘船急切地装载其所有航行。我不想这样做,因为性能影响(每艘船最终可能有很多航行)
- 首先加载所有船只,然后
foreach
结果列表,并为每艘船执行单独的查询以加载其"最新"航行。尽管这有效,但与单个查询相比,它似乎效率低下。
然后,我想我可以向Ship
实体添加一个导航属性,该属性将用于直接引用"最新"航行。所以我试了这个:
public class Ship
{
public int Id { get; set; }
public virtual ICollection<Voyage> Voyages { get; set; }
public int? MostRecentVoyageId { get; set; } <-- new property added
public virtual Voyage MostRecentVoyage { get; set; } <-- new property added
}
我使MostRecentVoyageId
为空,因为一艘船在首次创建时根本不会有任何航行。
这在数据库中给了我以下内容:
CREATE TABLE [dbo].[Ship] (
[Id] INT IDENTITY (1, 1) NOT NULL,
[MostRecentVoyageId] INT NULL,
CONSTRAINT [PK_dbo.Ship] PRIMARY KEY CLUSTERED ([Id] ASC),
CONSTRAINT [FK_dbo.Ship_dbo.Voyage_MostRecentVoyageId] FOREIGN KEY ([MostRecentVoyageId]) REFERENCES [dbo].[Voyage] ([Id])
);
这对于Ship
表很好,但我在Voyage
表中得到了这个:
CREATE TABLE [dbo].[Voyage] (
[Id] INT IDENTITY (1, 1) NOT NULL,
[ShipId] INT NOT NULL,
[Started] DATETIMEOFFSET (7) NOT NULL,
[Ship_Id] INT NULL,
CONSTRAINT [PK_dbo.Voyage] PRIMARY KEY CLUSTERED ([Id] ASC),
CONSTRAINT [FK_dbo.Voyage_dbo.Ship_ShipId] FOREIGN KEY ([ShipId]) REFERENCES [dbo].[Ship] ([Id]) ON DELETE CASCADE,
CONSTRAINT [FK_dbo.Voyage_dbo.Ship_Ship_Id] FOREIGN KEY ([Ship_Id]) REFERENCES [dbo].[Ship] ([Id])
);
请注意额外的Ship_Id
列和额外的外键关系。我很确定不需要此专栏,但我找不到摆脱它的方法。我尝试使用 [ForeignKey]
和 [InverseProperty]
属性,但这些只是给了我一个例外。
这是否是我无法使用 EF 数据批注配置的关系类型?我必须为此使用流畅的语法吗?无论如何,我做错了吗:有没有更好的方法来使用我的原始实体类型执行 LINQ 查询?
我尝试在SO上寻找有类似问题的人,我发现了这个,这个和这个,但这些似乎都没有帮助。
我有几乎完全相同的模型,但在不同的上下文中,我通过执行等效的操作[ForeignKey("MostRecentVoyage")]
将属性添加到MostRecentVoyageId
并将[InverseProperty("Ship")]
属性添加到Ship
实体/类上的Voyages
属性,摆脱了数据库中不需要的列。