这是我关于SO的第一个问题。
我有一个ASP.NET 4.0 MVC3项目,它使用EF Code First作为ORM和FluentMigrator进行版本迁移。我有Message实体类,如下所示:
public class Message
{
[Key]
[Column("Message_Id")]
public int Id { get; set; }
public DateTime CreatedTime { get; set; }
[Required]
[StringLength(MaxSubjectLength)]
public string Subject { get; set; }
[Required]
[StringLength(MaxBodyLength)]
public string Body { get; set; }
public Link Link { get;set; }
}
没有定义自定义映射,MSSQL Server 2012数据库表消息:
CREATE TABLE [dbo].[Messages](
[Message_Id] [int] IDENTITY(1,1) NOT NULL,
[CreatedTime] [datetime] NOT NULL,
[Subject] [nvarchar](78) NOT NULL,
[BodyHtml] [nvarchar](2000) NOT NULL,
[Link_Id] [int] NULL,
[Collection_Id] [int] NULL,
[MessageType] [nvarchar](30) NOT NULL,
CONSTRAINT [PK_Messages] PRIMARY KEY CLUSTERED
(
[Message_Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[Messages] WITH CHECK ADD CONSTRAINT [FK_Messages_Collection] FOREIGN KEY([Collection_Id])
REFERENCES [dbo].[Collections] ([Id])
GO
ALTER TABLE [dbo].[Messages] CHECK CONSTRAINT [FK_Messages_Collection]
GO
ALTER TABLE [dbo].[Messages] WITH CHECK ADD CONSTRAINT [FK_Messages_Link] FOREIGN KEY([Link_Id])
REFERENCES [dbo].[Links] ([Id])
GO
ALTER TABLE [dbo].[Messages] CHECK CONSTRAINT [FK_Messages_Link]
GO
Link_Id,Collection_Id和MessageType列在该表中,因为我想进行TPH继承(实际上我想做TPT继承然后切换到TPH,我猜它会解决我的问题,但事实并非如此)。
现在我希望LinkMessage和CollectionMessage类看起来像这样:
public class LinkMessage : Message
{
public int? Link_Id { get;set; }
}
public class CollectionMessage : Message
{
public int? Collection_Id { get;set; }
}
所以问题是:当我删除链接导航属性时,即使没有定义继承(即只存在消息实体),重建我的项目,(甚至重新创建数据库等)并做简单查询var a = DBContext.Messages.ToList();
EF生成以下查询:
SELECT
[Extent1].[Message_Id] AS [Message_Id],
[Extent1].[CreatedTime] AS [CreatedTime],
[Extent1].[Subject] AS [Subject],
[Extent1].[BodyHtml] AS [BodyHtml],
[Extent1].[Link_Id] AS [Link_Id]
FROM [dbo].[Messages] AS [Extent1]
为什么它仍然包含Link_Id?它不再是模型了。这将导致继承问题 - 当我使用上面的子类进行TPH(或TPT)继承时,app失败并显示错误'无效列名'Link_Id1''。为什么选择Link_Id1?怎么会发生这种情况?我完全糊涂了。 'Collection_Id'列表现正常。这两列是相同的。 我试图重新创建数据库,杀死了应用程序的所有进程(甚至重新启动了我的电脑),尝试加载以前的版本并删除了Message.Link_Id属性,尝试使用数据而没有 - 相同的行为。我尝试谷歌的东西,但最终没有任何东西,因为实际上我甚至不知道如何进行正确的搜索查询和搜索我没有给我什么,我花了第二天与它...
以下是他们正在生成的查询和SQL:
var b = DBContext.Messages.OfType<CollectionMessage>();
SELECT
[Extent1].[Message_Id] AS [Message_Id],
'0X0X' AS [C1],
[Extent1].[CreatedTime] AS [CreatedTime],
[Extent1].[Subject] AS [Subject],
[Extent1].[BodyHtml] AS [BodyHtml],
[Extent1].[Collection_Id] AS [Collection_Id],
[Extent1].[Link_Id1] AS [Link_Id1]
FROM [dbo].[Messages] AS [Extent1]
WHERE [Extent1].[MessageType] = N'CollectionMessage'
var b = DBContext.Messages.OfType<LinkMessage>();
SELECT
[Extent1].[Message_Id] AS [Message_Id],
'0X0X' AS [C1],
[Extent1].[CreatedTime] AS [CreatedTime],
[Extent1].[Subject] AS [Subject],
[Extent1].[BodyHtml] AS [BodyHtml],
[Extent1].[Link_Id] AS [Link_Id],
[Extent1].[Link_Id1] AS [Link_Id1]
FROM [dbo].[Messages] AS [Extent1]
WHERE [Extent1].[MessageType] = N'LinkMessage'
var b = DBContext.Messages;
SELECT
[Extent1].[Message_Id] AS [Message_Id],
CASE WHEN (((CASE WHEN ([Extent1].[MessageType] = N'LinkMessage') THEN cast(1 as bit) ELSE cast(0 as bit) END) <> cast(1 as bit)) AND ((CASE WHEN ([Extent1].[MessageType] = N'CollectionMessage') THEN cast(1 as bit) ELSE cast(0 as bit) END) <> cast(1 as bit))) THEN '0X' WHEN ([Extent1].[MessageType] = N'LinkMessage') THEN '0X0X' ELSE '0X1X' END AS [C1],
[Extent1].[CreatedTime] AS [CreatedTime],
[Extent1].[Subject] AS [Subject],
[Extent1].[BodyHtml] AS [BodyHtml],
CASE WHEN (((CASE WHEN ([Extent1].[MessageType] = N'LinkMessage') THEN cast(1 as bit) ELSE cast(0 as bit) END) <> cast(1 as bit)) AND ((CASE WHEN ([Extent1].[MessageType] = N'CollectionMessage') THEN cast(1 as bit) ELSE cast(0 as bit) END) <> cast(1 as bit))) THEN CAST(NULL AS int) WHEN ([Extent1].[MessageType] = N'LinkMessage') THEN [Extent1].[Link_Id] END AS [C2],
CASE WHEN (((CASE WHEN ([Extent1].[MessageType] = N'LinkMessage') THEN cast(1 as bit) ELSE cast(0 as bit) END) <> cast(1 as bit)) AND ((CASE WHEN ([Extent1].[MessageType] = N'CollectionMessage') THEN cast(1 as bit) ELSE cast(0 as bit) END) <> cast(1 as bit))) THEN CAST(NULL AS int) WHEN ([Extent1].[MessageType] = N'LinkMessage') THEN CAST(NULL AS int) ELSE [Extent1].[Collection_Id] END AS [C3],
[Extent1].[Link_Id1] AS [Link_Id1]
FROM [dbo].[Messages] AS [Extent1]
为什么要查询Link_Id和Link_Id1列?有人帮帮我,我错过了什么?
UPD:当我删除自定义DBInitializer并使Code First为我创建数据库时,它会在“消息”表中创建额外的列“Link_Id1”。
答案 0 :(得分:6)
Link
实体中包含Messages
的集合。这个属性使用不多,所以我们没有注意到其他属性。当我删除它 - 一切都开始工作。
所以它开始就像一个非常有趣的问题,但结果是非常明显的答案 - 人为因素。
对于所有刚接触EF并且正在同一个洞穴中的人,我会留下一个注释:EF除非有参考,否则不能“记住”某些属性。因此,如果您遇到类似的麻烦,请仔细检查您的代码,并请其他人这样做,没有魔力!
我现在不能upvote,所以 soadyp , Gert Arnold - 感谢您的帮助!
答案 1 :(得分:4)
答案 2 :(得分:2)
如果您的模型中只有Message
和(必需Link
),始终将成为Link_Id
列以容纳Message.Link
。
如果您还在模型中关闭了Message
的派生类,则始终生成的Link_Id
用于属性LinkMessage.Link_Id
。您不能在基类中使用Link
,在派生类中使用外键值。现在EF只是注意到有一个属性LinkMessage.Link_Id
,除此之外还有一个导航属性Link
。对于后者,Link_Id
不再可用,因此EF创建了Link_Id1
。
您必须将Link
和Link_Id
放在同一个类中,以使EF将它们视为一个外键的两个部分(外键关联)。它看起来像这样:
public class LinkMessage : Message
{
[ForeignKey("Link")]
public int? Link_Id { get; set; }
public Link Link { get; set; }
}