【发布时间】:2014-11-18 11:12:36
【问题描述】:
我正在使用 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
);
到目前为止,一切都很好。当我想查询这些数据时,问题就来了。我需要获取船舶列表,但对于每艘船,我还需要最近的航次。尽管我可以使用 SQL 编写这样的查询,但我看不到一种编写 LINQ 查询的方法可以一键完成。
此时我的选择似乎是:
- 装载所有船只,并为每艘船急切装载其所有航次。我不想这样做,因为会影响性能(每艘船最终可能有很多航程)
- 首先加载所有船只,然后
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 查询?
【问题讨论】:
-
我有几乎完全相同的模型,但在不同的上下文中,我通过将
[ForeignKey("MostRecentVoyage")]添加到 MostRecentVoyageId 并将[InverseProperty("Ship")]添加到航程来删除数据库中不需要的列。这是否会引发您提到的异常,如果是,那么异常是什么。 -
@BenRobinson 我刚刚尝试过,它不会引发异常,但我仍然会在
Voyage表上得到额外的Ship_Id列。我假设[InverseProperty]属性应该出现在航程的ShipId属性上? -
不应该将
[InverseProperty("Ship")]放在Ship实体上的Voyages属性上。 -
@BenRobinson 太棒了!这就像一个魅力。非常感谢。您能否将您的评论放入答案中以便我接受?
-
按要求将其添加为答案。
标签: c# linq entity-framework ef-code-first