我注意到 EF 发生了一个奇怪的事件,以及它确定在评估.Include(x=x.T)
语句时将使用哪些字段的方式。
在我们的项目中,我们所有的数据库表(以及 EF 的 POCO)都以 为前缀DB
,而有问题的两个表是DBTemplates
和DBItems
。每个项目都有一个(可选)与之关联的模板。
我们的相关行DbContext
是这样的:
public IDbSet<DBTemplate> Templates {get;set;}
public IDbSet<DBItem> Items {get;set;}
为简单起见,假设两个表都包含 Id 和 Name 属性,并且 DBItems 表具有返回 DBTemplates 的外键。这(现在)被称为Template_Id
(我稍后会解释为什么),但一般来说我们会命名它DBTemplateId
。
一般来说,我们有一个名为的变量db
,它是我们的DbContext
.
最初创建DBItem
定义的开发人员添加了链接到相关的虚拟属性DBTemplate
,但省略了外键。属性如下所示:
public class DBItem
{
public System.Guid Id { get; set; }
public string Name { get; set; }
public virtual DBTemplate Template { get; set; }
}
public class DBTemplate
{
public System.Int32 Id { get; set; }
public string Name { get; set; }
}
查询 Items 时,命中以下语句
db.Items.Include(i=>i.Template)
并生成以下SQL
SELECT
[Extent1].[Id] AS [Id],
[Extent1].[Name] AS [Name],
[Extent2].[Id] AS [Id1],
[Extent2].[Name] AS [Name1],
FROM [dbo].[DBItems] AS [Extent1]
LEFT OUTER JOIN [dbo].[DBTemplates] AS [Extent2] ON [Extent1].[Template_Id] = [Extent2].[Id]
到目前为止,一切都很好。但是,我现在需要(并且应该拥有)Template_Id
DBItem 对象。当我将它添加到 DBItem 类时,它看起来像这样:
public class DBItem
{
public System.Guid Id { get; set; }
public string Name { get; set; }
public Nullable<System.Int32> Template_Id { get; set; }
public virtual DBTemplate Template { get; set; }
}
命中同一行,但生成的 SQL 现在看起来像这样
SELECT
[Extent1].[Id] AS [Id],
[Extent1].[Name] AS [Name],
[Extent1].[Template_Id] AS [Template_Id],
[Extent2].[Id] AS [Id1],
[Extent2].[Name] AS [Name1],
FROM [dbo].[DBItems] AS [Extent1]
LEFT OUTER JOIN [dbo].[DBTemplates] AS [Extent2] ON [Extent1].[Template_Id1] = [Extent2].[Id]
(注意连接的Template_Id1)
现在,这很容易解决,因为我可以告诉模型构建器查找名为 的外键Template_Id
,一切都很好。
builder.Entity<DBItem>().HasOptional(x => x.Template).WithMany().HasForeignKey(x => x.Template_Id);
但是,如果遵循约定,并且名为 的字段DBTemplateId
和 DBItem 类看起来像这样:
public class DBItem
{
public System.Guid Id { get; set; }
public string Name { get; set; }
public Nullable<System.Int32> DBTemplateId { get; set; }
public virtual DBTemplate Template { get; set; }
}
在不修改 ModelBuilder 属性的情况下(让 EF 完全按照惯例接管),它正确地将 DBTemplateId 属性作为外键,并生成如下 SQL:
SELECT
[Extent1].[Id] AS [Id],
[Extent1].[Name] AS [Name],
[Extent1].[DBTemplateId] AS [DBTemplateId],
[Extent2].[Id] AS [Id1],
[Extent2].[Name] AS [Name1],
FROM [dbo].[DBItems] AS [Extent1]
LEFT OUTER JOIN [dbo].[DBTemplates] AS [Extent2] ON [Extent1].[DBTemplateId] = [Extent2].[Id]
并且查询按预期工作(也不易出错的 C# 代码)。
显然这里的教训是遵循字段命名的模式,但是为什么 EF 在命名时会自动查找并使用属性名称作为连接键,但在 POCO 上存在时会DBTemplateId
查找重复项?Template_Id1
Template_Id