本篇随筆目錄:
在關系資料庫中,不同表之間往往不是全部都單獨存在,而是互相存在關聯的。兩個不同表之間可以存在外鍵依賴關系,一個表自身也可以有自反關系(表中的一個字段引用主鍵,進而也是外鍵字段)。
Entity Framework Code First預設多重關系的一些約定規則:
一對多關系:兩個類中分别包含一個引用和一個集合屬性,也可以是一個類包含另一個類的引用屬性,或一個類包含另一個類的集合屬性。如在本篇接下來用到的例子Category類和Product類,要使得Category與Product之間具有一對多關系,Entity Framework Code First可以有3種展現方式:
1>、在Category類中定義ICollection<Product> Products集合屬性,同時在Product類中定義Category Category引用屬性。
2>、僅在Category類中定義ICollection<Product> Products集合屬性。
3>、僅在Product類中定義Category Category引用屬性。
多對多關系:兩個類分别包含對方的一個集合屬性。如在本篇接下來用到的例子User類和Role類,要使得User與Role之間具有多對多關系,即一個使用者可以屬于多個角色,一個角色可以有多個使用者,則需要在User類中需要定義一個ICollection<Role> Roles集合屬性,同時在Role類中需要定義一個ICollection<User> Users屬性。
一對一關系:兩個類分别包含對方的一個引用屬性。如在本篇接下來用到的例子User類和UserProfile類,要使得User與UserProfile之間具有一對一關系,則需要在User類中定義一個UserProfile UserProfile的引用屬性,同時在UserProfile類中定義一個User User的引用屬性。
下面具體描述Entity Framework Code First生成外鍵的預設約定,并通過執行個體展示Entity Framework Code First處理一個表及多個表之間的關系。
1、外鍵列名預設約定
Entity Framework Code First在根據預設約定建立外鍵時,外鍵列的名稱存在3種方式。在《Programming Entity Framework Code First》一書中,給出的3種外鍵列名的約定方式是:[Target Type Key Name], [Target Type Name] + [Target Type Key Name], or [Navigation Property Name] + [Target Type Key Name],對應的中文翻譯為:[目标類型的鍵名],[目标類型名稱]+[目标類型鍵名稱],或[引用屬性名稱]+[目标類型鍵名稱]。
Entity Framework Code First外鍵預設限制生成的外鍵在分别滿足3種不同的條件下,外鍵列名有3種不同的命名規則。且經過測試這3種不同的外鍵名稱命名之間存在優先級:[目标類型的鍵名] > [引用屬性名稱]+[目标類型鍵名稱] > [目标類型名稱]+[目标類型鍵名稱]。接下來以Product類及Category類為例,分别測試外鍵列名稱的3中不同生成方式,Category與Product為一對多關系。
1>、[目标類型的鍵名]
這種方式為要求在Product表中外鍵列名與Category表中的主鍵列名相同,是以也就要求在Product類中有定義與Category類中作為主鍵的屬性。如在Category類中主鍵屬性為CategoryID,則需要在Product類中也定義一個CategoryID的屬性。
檔案Category.cs:

View Code
檔案Product.cs:

說明:在Category類及Product類中的引用屬性及集合屬性前加virtual修飾,為的是Entity Framework Code First的延遲加載功能。不使用virtual修飾,在Category類的一個執行個體要查詢包含的Product執行個體時,将不會啟用延遲加載。當然Entity Framework Code First延遲加載并不是必須的,是以virtual修飾符也可以不加。
在定義以上兩個類之後,不再添加任何的Entity Framework Code First與資料庫的映射配置,運作之後,生成的資料表結構為:
從生成的Categories與Products表結構可以看出,在Products表中的外鍵CategoryID與引用的表Categories主鍵名稱相同。跟蹤Entity Framework Code First生成資料表的執行腳本可以看到具體生成外鍵的SQL語句。
同時,從生成外鍵的腳本還能看出一點,Entity Framework Code First生成外鍵是啟用級聯删除功能的。即當删除Categories表中一條記錄時,資料庫會自動聯帶删除Products表中屬于該類别的記錄。
在資料庫中生成的Products表,檢視外鍵FK_dbo.Products_dbo.Categories_CategoryID屬性,其的确有啟用級聯删除功能。
2>、[目标類型名稱]+[目标類型鍵名稱]
這種方式要求在Product表中外鍵列名為Category類名+Category類中鍵名稱,即在Products表中生成的外鍵名稱為Category_CategoryID。示例:在Category類中添加ICollection<Product> Products的集合屬性,而在Product類中不做任何與Category關聯的代碼,也不定義CategoryID屬性。

檔案Product.cs:

3>、[引用屬性名稱]+[目标類型鍵名稱]
這種方式為要求在Product表中外鍵列名為在Product類中引用Category的屬性名稱 + Category類的主鍵名稱。如:在Product類中定義一個Category屬性Cat,則生成的外鍵名稱為Cat_CategoryID。


關于3種不同的外鍵名稱命名之間存在優先級:[目标類型的鍵名] > [引用屬性名稱]+[目标類型鍵名稱] > [目标類型名稱]+[目标類型鍵名稱]的測試方法:
[目标類型的鍵名]的最高優先級:隻要在Product類中定義了CategoryID的屬性,在Products表中生成的外鍵列名都隻會為CategoryID。
[引用屬性名稱]+[目标類型鍵名稱] > [目标類型名稱]+[目标類型鍵名稱]:隻要在Product類中定義Cat屬性,不管Category類中是否定義Products屬性,生成的Products表中外鍵都隻會是Cat_CategoryID。
2、一對多關系
Entity Framework Code First在根據定義的類生成資料表時,資料表之間的外鍵關系及所生成的外鍵列名有預設的約定。但這種約定同樣可以進行修改,如将不滿足預設外鍵約定的屬性來作為生成表的外鍵。示例:Category類與Product類,在Product類中定義一個屬性CatID,要将CatID屬性作為Product的引用Category的外鍵,而按照Entity Framework Code First的預設約定是不會的。要做到需要的CatID作為外鍵,同樣可以使用Data Annotations和Fluent API兩種方式實作。
1>、Data Annotations方式
檔案類Category.cs:

檔案類Product.cs:

檢視Products表的外鍵咧CatID引用關系:
其中,在Product類中,為設定CatID屬性為外鍵的代碼為:
該段實作方式還可以改為:
2>、Fluent API方式
檔案類Category.cs:


檔案類PortalContext.cs:

說明:在PortalContext.cs的OnModelCreating方法中,對兩個實體類Category及Product均添加了Fluent API形式的關系配置。對于Entity Framework Code First而言,兩個實體類之間的關系,可以兩個類中均添加關系映射配置,也可以隻對其中任意一個實體類添加關系映射配置。即在PortalContext.cs的OnModelCreating方法中可以隻包含Category或隻包含Product類的關系映射配置。這裡從Entity Framework Code First的使用經驗及技巧,建議将實體類之間關系映射配置在包含外鍵的類中。即OnModelCreating中隻添加對Product實體類的關系映射配置,這樣做有一個好處,當Category有多個表引用它時,可以将外鍵均配置在引用它的實體類中,進而降低Category類的複雜度,同時也有益于代碼的維護。
即在PortalContext.cs的OnModelCreating方法隻需下面的定義即可:
Entity Framework Code First根據一對多關系關系生成的外鍵引用限制預設是有級聯删除的,可以通過以下方式禁用Category與Product之間的級聯删除。
也可以在Entity Framework Code First生成的全部表中都統一設定禁用一對多級聯删除。
雖然Entity Framework Code First是可以支援外鍵列名自定義的,但在實際的項目中,更多的外鍵列名稱還是與所引用表的主鍵列名相同。即在Category表中主鍵為CategoryID,在Product表中外鍵列名稱還是為CategoryID。
Entity Framework Code First的Fluent API配置實體類與表的映射關系,還可以将所有的實體類與表的映射全部寫在一個類中,這樣可以友善代碼的維護。

檔案類CategoryMap.cs,用于描述Category類與生成的表之間的映射關系:


檔案類ProductMap.cs,用于描述Product類與生成的表之間的映射關系:

PortalContext.cs的OnModelCreating方法:

3、一對一關系
在一對一關系中,兩個表均有各自的主鍵,但要看哪個表的主鍵同時作為外鍵引用另一個表的主鍵。示例以User類與UserProfile類作為兩個具有一對一關系的類,其中User類包含作為主鍵的UserID屬性,UserProfile類包含作為主鍵的ProfileID的屬性。
檔案類User.cs:

檔案類UserProfile.cs:

在生成的資料表中,UserProfile表中的主鍵ProfileID同時也作為外鍵引用User表中的主鍵UserID。
修改檔案類User.cs:

修改檔案類UserProfile.cs:

則實體類執行之後生成的資料表User主鍵UserID将同時作為外鍵引用UserProfile表的主鍵ProfileID。
Fluent API設定實體類生成的表引用與被引用通過WithRequiredPrincipal、WithRequiredDependent及WithOptionalPrincipal、WithOptionalDependent來設定,使用Principal屬性的實體類将被另外的實體類生成的表引用,使用Dependent屬性的實體類将引用另外的實體類。

映射檔案類UserMap.cs:


映射檔案類UserProfileMap.cs:

在以上實體類及實體映射類執行以後,生成的資料表結構如下:
在生成的表結構中,UserProfile表中的主鍵UserID同時也作為外鍵引用User表的主鍵UserID。若修改UserProfileMap.cs如下,則生成的表結構User表的主鍵UserID将作為你外鍵引用UserProfile表的主鍵UserID。

4、多對多關系
Entity Framework Code First在根據定義的多對多關系的類生成資料表時,除了生成實體類定義的屬性表之外,還會生成一個中間表。用于展現兩個實體表之間的多對多的關系。示例實體類User與Role為多對多關系,一個使用者可以屬于多個角色,一個角色可以包含多個使用者。

檔案類Role.cs:

從以上的表結構中,可以看出,實體類運作之後,除了生成Users表和Roles表之外,還生成了RoleUsers表作為中介表,展現Users表和Roles表之間的多對多關聯關系。中介表RoleUsers的字段生成規則按照 [目标類型名稱]+[目标類型鍵名稱] 的約定。
Entity Framework Code First根據預設約定生成的多對多關聯關系的表時,預設啟用多對多的資料級聯删除,可以添加代碼進行禁用。
FluentAPI實作方式:



映射檔案類RoleMap.cs:

運作之後生成的表結構:
5、一對多自反關系
一對多自反關系,即一個表存在一個外鍵列引用自身的主鍵。在項目中,最常見的一對多自反關系為分類表,分類表通過一個ParentID列儲存引用主鍵,已實作無限級遞歸。
Fluent API實作方式:
映射檔案類CategoryMap.cs:
以上代碼在運作之後,生成的資料表:
6、多對多自反關系
多對多關系示例:Family中一條記錄可能有多個Parents,也可能有多個Children。
檔案類Family.cs:

映射檔案類FamilyMap.cs:

Family類及映射配置類,在運作之後生成的資料表結構:
在上面的表結構中,指定Family之間的中介表為FamilyRelationship,其中FamilyRelationship的兩個字段ParentID及ChildID均引用Familyi表中的FamilyID作為外鍵。可能在實際的項目過程中,出現這種多對多自反引用關系的情況比較少見。
http://www.cnblogs.com/libingql/p/3353112.html
學習交流群:364976091