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