是否可以使用实体框架代码优先数据批注创建双向外键关系

本文关键字:创建 关系 数据 实体 可以使 框架 代码 是否 | 更新日期: 2023-09-27 18:30:59

我正在使用 EF 6 Code First 并试图找出使用数据注释配置关系的最佳方法,但由于某种原因,EF 正在向架构添加我不想要的额外列。

我有两个实体:ShipVoyage。一艘船可以有很多航程;一次航行属于一艘船,也只属于一艘船。所以我从这个开始(出于 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属性,摆脱了数据库中不需要的列。