2013-01-31 18 views
0

我注意到EF出現了一個奇怪的事件,並且它確定了在評估.Include(x=x.T)語句時它將使用哪些字段。確定連接表外鍵列名的約定

在我們的項目中,我們所有的數據庫表(以及EF的POCO)的前綴都是DB,所涉及的兩個表格分別是DBTemplatesDBItems。每個項目都有一個與其關聯的(可選)模板。

相關的線在我們DbContext此:

public IDbSet<DBTemplate> Templates {get;set;} 
public IDbSet<DBItem> Items {get;set;} 

爲簡單起見,假設這兩個表包含一個標識和名稱屬性以及具有一個外鍵回到DBTemplates的DBItems表。這是(現在)叫做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; } 
} 

當查詢項目,下面的語句被擊中

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] 

到目前爲止,一切都很好。但是,我現在需要(也應該有)DBItem對象上的Template_Id。當我把它添加到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; } 
} 

,而無需修改模型構建器屬性(讓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自動查找和使用屬性名作爲連接鍵命名爲DBTemplateId,但查找重複Template_Id1當存在於POCO Template_Id什麼時候?

回答

2

當實體框架代碼首先檢測到關係時,它會嘗試通過遵循一些約定來發現應該用作外鍵的屬性。

它會尋找匹配的以下3個規則,一個名稱的屬性:

  • [Targettype key property name]
  • [Targettype name]+[Targettype key property name]
  • [Foreignkey navigation property name]+[Targettype key property name]

所以,當你添加一個名爲DBTemplateId屬性第二個規則將被匹配,Code First將正確使用該屬性作爲外鍵並生成一個外鍵具有該名稱的數據庫中的關鍵列。

如果您改爲調用屬性Template_Id,則不會匹配任何規則,Code First將自動爲外鍵生成一個名稱。

按照慣例,生成的名稱將是[Navigation property name]_[Targettype key property name],在您的情況下會生成Template_Id。但是因爲已經存在一個名稱屬性,所以它會附加數字1.

在我看來,Code First有一個第四條規則可以與它自己創建的同名相匹配。如果是這樣的話,Template_Id應該是合法的名稱,並且您不必使用HasForeignKey方法對其進行配置。

+0

對我來說是有意義的,有沒有一個地方這是記錄? –

+0

我不知道官方的Microsoft文檔在哪裏。我已經在「程序設計實體框架:代碼優先」一書中詳細閱讀了它,在這裏很好地解釋了這一點。 –