C# 我可以使用实体框架代码优先数据注释创建双向外键关系吗?

C# 我可以使用实体框架代码优先数据注释创建双向外键关系吗?,c#,linq,entity-framework,ef-code-first,C#,Linq,Entity Framework,Ef Code First,我首先使用EF 6代码,并试图找出使用数据注释配置关系的最佳方法,但出于某种原因,EF正在向模式中添加我不想要的额外列 我有两个实体:船舶和航行。一艘船可以进行多次航行;一次航行只属于一艘船。因此,我从以下内容开始(为了方便起见,将其简化): 到目前为止,一切顺利。当我想查询这些数据时,问题就来了。我需要获得一份船舶清单,但对于每艘船舶,我还需要最新的航行。我看不到一种编写LINQ查询的方法可以一次性完成这项工作,即使我可以使用SQL编写这样的查询 在这一点上,我的选择似乎是: 装载所有船舶,

我首先使用EF 6代码,并试图找出使用数据注释配置关系的最佳方法,但出于某种原因,EF正在向模式中添加我不想要的额外列

我有两个实体:
船舶
航行
。一艘船可以进行多次航行;一次航行只属于一艘船。因此,我从以下内容开始(为了方便起见,将其简化):

到目前为止,一切顺利。当我想查询这些数据时,问题就来了。我需要获得一份船舶清单,但对于每艘船舶,我还需要最新的航行。我看不到一种编写LINQ查询的方法可以一次性完成这项工作,即使我可以使用SQL编写这样的查询

在这一点上,我的选择似乎是:

  • 装载所有船舶,并为每艘船舶急切装载其所有航程。我不想这样做,因为这会影响性能(每艘船最终可能有很多航程)
  • 首先加载所有船舶,然后在生成的列表上加载foreach,并对每艘船舶执行单独的查询以加载其“最近”的航程。尽管这样做有效,但与单个查询相比,它似乎有点低效
然后,我想我可以向
船舶
实体添加一个导航属性,用于直接引用“最近”的航行。所以我试了一下:

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
}
这对于
Ship
表很好,但是对于
seave
表,我得到了以下信息:

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查询


我试着寻找有类似问题的人,我发现了,但这些似乎都没有帮助。

我有几乎完全相同的模型,但在不同的环境中,我通过添加
[ForeignKey(“MostRecentVoyage”)]
属性添加到
MostRecentVoyageId
并添加
[反向属性(“船舶”)]
属性添加到
船舶
实体/类别上的
航程
属性。

我拥有几乎完全相同的模型,但在不同的上下文中,通过将
[ForeignKey(“MostRecentVoyage”)]
添加到MostRecentVoyageId并添加
[InverseProperty(“Ship”)]
到voyages。这会引发您提到的异常吗?如果是,会引发什么异常。@BenRobinson我刚刚尝试过,它不会引发异常,但我仍然在
Voyage
表上有额外的
Ship\u Id
列。我假设
[InverseProperty]
属性应位于航次的
ShipId
属性上?否
[反向属性(“船舶”)]
应该在
Ship
实体上的
Voyages
属性上运行。@BenRobinson真棒!这就像一个符咒。非常感谢。你能把你的评论放到一个答案中让我接受吗?按要求添加它作为一个答案。我已经找到了一种方法,可以执行一个LINQ查询,在一次“点击”中获取数据但是生成的SQL比使用
MostRecentVoyage
外键时更复杂,即
ship.MostRecentVoyage
vs
ship.Voyages.OrderByDescending(x=>x.Started).FirstOrDefault()
。您认为显式外键是一个糟糕的架构决策,还是为了获得更高性能的查询而增加维护成本值得?(假设它更高性能,我还没有进行基准测试)如果动态计算值对于您的目的来说足够快,那么就这样做。这样您就不必担心确保外键总是正确的,正如您所说的,这可能是一个维护难题。
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
}
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])
);
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])
);