Entity Framework Code First关系映射约定
本篇隨筆目錄:
1、外鍵列名默認約定
2、一對多關系
3、一對一關系
4、多對多關系
5、一對多自反關系
6、多對多自反關系
?
在關系數據庫中,不同表之間往往不是全部都單獨存在,而是相互存在關聯的。兩個不同表之間可以存在外鍵依賴關系,一個表自身也可以有自反關系(表中的一個字段引用主鍵,從而也是外鍵字段)。
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:
using System; using System.Collections.Generic; using System.Linq; using System.Text;namespace Portal.Entities {public class Category{public int CategoryID { get; set; }public string CategoryName { get; set; }public virtual ICollection<Product> Products { get; set; }} } View Code文件Product.cs:
using System; using System.Collections.Generic; using System.Linq; using System.Text;namespace Portal.Entities {public class Product{public int ProductID { get; set; }public string ProductName { get; set; }public decimal UnitPrice { get; set; }public int CategoryID { get; set; }public virtual Category Category { get; set; }} } View Code說明:在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語句。
ALTER TABLE [dbo].[Products] ADD CONSTRAINT [FK_dbo.Products_dbo.Categories_CategoryID] FOREIGN KEY ([CategoryID]) REFERENCES [dbo].[Categories] ([CategoryID]) ON DELETE CASCADE同時,從生成外鍵的腳本還能看出一點,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屬性。
文件Category.cs:
using System; using System.Collections.Generic; using System.Linq; using System.Text;namespace Portal.Entities {public class Category{public int CategoryID { get; set; }public string CategoryName { get; set; }public virtual ICollection<Product> Products { get; set; }} } View Code文件Product.cs:
using System; using System.Collections.Generic; using System.Linq; using System.Text;namespace Portal.Entities {public class Product{public int ProductID { get; set; }public string ProductName { get; set; }public decimal UnitPrice { get; set; }} } View Code在定義以上兩個類之后,不再添加任何的Entity Framework Code First與數據庫的映射配置,運行之后,生成的數據表結構為:
? 3>、[引用屬性名稱]+[目標類型鍵名稱]
這種方式為要求在Product表中外鍵列名為在Product類中引用Category的屬性名稱 + Category類的主鍵名稱。如:在Product類中定義一個Category屬性Cat,則生成的外鍵名稱為Cat_CategoryID。
文件Category.cs:
using System; using System.Collections.Generic; using System.Linq; using System.Text;namespace Portal.Entities {public class Category{public int CategoryID { get; set; }public string CategoryName { get; set; }} } View Code文件Product.cs:
using System; using System.Collections.Generic; using System.Linq; using System.Text;namespace Portal.Entities {public class Product{public int ProductID { get; set; }public string ProductName { get; set; }public decimal UnitPrice { get; set; }/// <summary>/// 這里為演示,使用Cat作為Category的縮寫。/// </summary>public virtual Category Cat { get; set; }} } View Code在定義以上兩個類之后,不再添加任何的Entity Framework Code First與數據庫的映射配置,運行之后,生成的數據表結構為:
關于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:
using System; using System.Collections.Generic; using System.Linq; using System.Text;namespace Portal.Entities {public class Category{public int CategoryID { get; set; }public string CategoryName { get; set; }public virtual ICollection<Product> Products { get; set; }} } View Code文件類Product.cs:
using System; using System.Collections.Generic; using System.Linq; using System.Text;using System.ComponentModel.DataAnnotations.Schema;namespace Portal.Entities {public class Product{public int ProductID { get; set; }public string ProductName { get; set; }public decimal UnitPrice { get; set; }public int CatID { get; set; }[ForeignKey("CatID")]public virtual Category Category { get; set; }} } View Code在定義以上兩個類之后,不再添加任何的Entity Framework Code First與數據庫的映射配置,運行之后,生成的數據表結構為:
查看Products表的外鍵咧CatID引用關系:
其中,在Product類中,為設置CatID屬性為外鍵的代碼為:
public int CatID { get; set; } [ForeignKey("CatID")] public virtual Category Category { get; set; }該段實現方式還可以改為:
[ForeignKey("Category")] public int CatID { get; set; } public virtual Category Category { get; set; }2>、Fluent API方式
文件類Category.cs:
using System; using System.Collections.Generic; using System.Linq; using System.Text;namespace Portal.Entities {public class Category{public int CategoryID { get; set; }public string CategoryName { get; set; }public virtual ICollection<Product> Products { get; set; }} } View Code文件類Product.cs:
using System; using System.Collections.Generic; using System.Linq; using System.Text;namespace Portal.Entities {public class Product{public int ProductID { get; set; }public string ProductName { get; set; }public decimal UnitPrice { get; set; }public int CatID { get; set; }public virtual Category Category { get; set; }} } View Code文件類PortalContext.cs:
using System; using System.Collections.Generic; using System.Linq; using System.Text;using System.Data.Entity;using Portal.Entities;namespace Portal {public class PortalContext : DbContext{static PortalContext(){Database.SetInitializer(new DropCreateDatabaseIfModelChanges<PortalContext>());}public DbSet<Category> Categories { get; set; }public DbSet<Product> Products { get; set; }protected override void OnModelCreating(DbModelBuilder modelBuilder){modelBuilder.Entity<Category>().HasMany(t => t.Products).WithRequired(t => t.Category).HasForeignKey(d => d.CatID);modelBuilder.Entity<Product>().HasRequired(t => t.Category).WithMany(t => t.Products).HasForeignKey(d => d.CatID);}} } View Code說明:在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方法只需下面的定義即可:
protected override void OnModelCreating(DbModelBuilder modelBuilder) {modelBuilder.Entity<Product>().HasRequired(t => t.Category).WithMany(t => t.Products).HasForeignKey(d => d.CatID); }Entity Framework Code First根據一對多關系關系生成的外鍵引用約束默認是有級聯刪除的,可以通過以下方式禁用Category與Product之間的級聯刪除。
protected override void OnModelCreating(DbModelBuilder modelBuilder) {modelBuilder.Entity<Product>().HasRequired(t => t.Category).WithMany(t => t.Products).HasForeignKey(d => d.CatID).WillCascadeOnDelete(false); }也可以在Entity Framework Code First生成的全部表中都統一設置禁用一對多級聯刪除。
protected override void OnModelCreating(DbModelBuilder modelBuilder) {modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>(); }雖然Entity Framework Code First是可以支持外鍵列名自定義的,但在實際的項目中,更多的外鍵列名稱還是與所引用表的主鍵列名相同。即在Category表中主鍵為CategoryID,在Product表中外鍵列名稱還是為CategoryID。
Entity Framework Code First的Fluent API配置實體類與表的映射關系,還可以將所有的實體類與表的映射全部寫在一個類中,這樣可以方便代碼的維護。
文件類Category.cs:
using System; using System.Collections.Generic; using System.Linq; using System.Text;namespace Portal.Entities {public partial class Category{public Category(){this.Products = new List<Product>();}public int CategoryID { get; set; }public string CategoryName { get; set; }public virtual ICollection<Product> Products { get; set; }} } View Code文件類CategoryMap.cs,用于描述Category類與生成的表之間的映射關系:
using System; using System.Collections.Generic; using System.Linq; using System.Text;using System.ComponentModel.DataAnnotations.Schema; using System.Data.Entity.ModelConfiguration;using Portal.Entities;namespace Portal.Mapping {public class CategoryMap : EntityTypeConfiguration<Category>{public CategoryMap(){// Primary Keythis.HasKey(t => t.CategoryID);// Propertiesthis.Property(t => t.CategoryName).IsRequired().HasMaxLength(50);// Table & Column Mappingsthis.ToTable("Category");this.Property(t => t.CategoryID).HasColumnName("CategoryID");this.Property(t => t.CategoryName).HasColumnName("CategoryName");}} } View Code文件類Product.cs:
using System; using System.Collections.Generic;namespace Portal.Entities {public partial class Product{public int ProductID { get; set; }public int CategoryID { get; set; }public string ProductName { get; set; }public Nullable<decimal> UnitPrice { get; set; }public Nullable<int> Quantity { get; set; }public virtual Category Category { get; set; }} } View Code文件類ProductMap.cs,用于描述Product類與生成的表之間的映射關系:
using System; using System.Collections.Generic; using System.Linq; using System.Text;using System.ComponentModel.DataAnnotations.Schema; using System.Data.Entity.ModelConfiguration;using Portal.Entities;namespace Portal.Mapping {public class ProductMap : EntityTypeConfiguration<Product>{public ProductMap(){// Primary Keythis.HasKey(t => t.ProductID);// Propertiesthis.Property(t => t.ProductName).IsRequired().HasMaxLength(100);// Table & Column Mappingsthis.ToTable("Product");this.Property(t => t.ProductID).HasColumnName("ProductID");this.Property(t => t.CategoryID).HasColumnName("CategoryID");this.Property(t => t.ProductName).HasColumnName("ProductName");this.Property(t => t.UnitPrice).HasColumnName("UnitPrice");this.Property(t => t.Quantity).HasColumnName("Quantity");// Relationshipsthis.HasRequired(t => t.Category).WithMany(t => t.Products).HasForeignKey(d => d.CategoryID);}} } View CodePortalContext.cs的OnModelCreating方法:
protected override void OnModelCreating(DbModelBuilder modelBuilder) {modelBuilder.Configurations.Add(new CategoryMap());modelBuilder.Configurations.Add(new ProductMap()); } View Code3、一對一關系
? 在一對一關系中,兩個表均有各自的主鍵,但要看哪個表的主鍵同時作為外鍵引用另一個表的主鍵。示例以User類與UserProfile類作為兩個具有一對一關系的類,其中User類包含作為主鍵的UserID屬性,UserProfile類包含作為主鍵的ProfileID的屬性。
1>、Data Annotations方式
文件類User.cs:
using System; using System.Collections.Generic;using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema;namespace Portal.Entities {public partial class User{[Key]public int UserID { get; set; }public string UserName { get; set; }public string Password { get; set; }public Nullable<bool> IsValid { get; set; }public virtual UserProfile UserProfile { get; set; }} } View Code文件類UserProfile.cs:
using System; using System.Collections.Generic;using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema;namespace Portal.Entities {public partial class UserProfile{[Key][ForeignKey("User")]public int ProfileID { get; set; }public string Name { get; set; }public Nullable<bool> Sex { get; set; }public Nullable<DateTime> Birthday { get; set; }public string Email { get; set; }public string Telephone { get; set; }public string Mobilephone { get; set; }public string Address { get; set; }public Nullable<DateTime> CreateDate { get; set; }public virtual User User { get; set; }} } View Code在定義以上兩個類之后,不再添加任何的Entity Framework Code First與數據庫的映射配置,運行之后,生成的數據表結構為:
在生成的數據表中,UserProfile表中的主鍵ProfileID同時也作為外鍵引用User表中的主鍵UserID。
修改文件類User.cs:
using System; using System.Collections.Generic;using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema;namespace Portal.Entities {public partial class User{[Key][ForeignKey("UserProfile")]public int UserID { get; set; }public string UserName { get; set; }public string Password { get; set; }public Nullable<bool> IsValid { get; set; }public virtual UserProfile UserProfile { get; set; }} } View Code修改文件類UserProfile.cs:
using System; using System.Collections.Generic;using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema;namespace Portal.Entities {public partial class UserProfile{[Key]public int ProfileID { get; set; }public string Name { get; set; }public Nullable<bool> Sex { get; set; }public Nullable<DateTime> Birthday { get; set; }public string Email { get; set; }public string Telephone { get; set; }public string Mobilephone { get; set; }public string Address { get; set; }public Nullable<DateTime> CreateDate { get; set; }public virtual User User { get; set; }} } View Code則實體類執行之后生成的數據表User主鍵UserID將同時作為外鍵引用UserProfile表的主鍵ProfileID。
2>、Fluent API方式
Fluent API設置實體類生成的表引用與被引用通過WithRequiredPrincipal、WithRequiredDependent及WithOptionalPrincipal、WithOptionalDependent來設置,使用Principal屬性的實體類將被另外的實體類生成的表引用,使用Dependent屬性的實體類將引用另外的實體類。
文件類User.cs:
using System; using System.Collections.Generic;using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema;namespace Portal.Entities {public partial class User{public int UserID { get; set; }public string UserName { get; set; }public string Password { get; set; }public Nullable<bool> IsValid { get; set; }public virtual UserProfile UserProfile { get; set; }} } View Code映射文件類UserMap.cs:
using System; using System.Collections.Generic; using System.Linq; using System.Text;using System.ComponentModel.DataAnnotations.Schema; using System.Data.Entity.ModelConfiguration;using Portal.Entities;namespace Portal.Mapping {public class UserMap : EntityTypeConfiguration<User>{public UserMap(){// Primary Keythis.HasKey(t => t.UserID);// Propertiesthis.Property(t => t.UserName).HasMaxLength(50);this.Property(t => t.Password).HasMaxLength(100);// Table & Column Mappingsthis.ToTable("User");this.Property(t => t.UserID).HasColumnName("UserID");this.Property(t => t.UserName).HasColumnName("UserName");this.Property(t => t.Password).HasColumnName("Password");this.Property(t => t.IsValid).HasColumnName("IsValid");}} } View Code文件類UserProfile.cs:
using System; using System.Collections.Generic;namespace Portal.Entities {public partial class UserProfile{public int UserID { get; set; }public string Name { get; set; }public Nullable<bool> Sex { get; set; }public Nullable<DateTime> Birthday { get; set; }public string Email { get; set; }public string Telephone { get; set; }public string Mobilephone { get; set; }public string Address { get; set; }public Nullable<DateTime> CreateDate { get; set; }public virtual User User { get; set; }} } View Code映射文件類UserProfileMap.cs:
using System; using System.Collections.Generic; using System.Linq; using System.Text;using System.ComponentModel.DataAnnotations.Schema; using System.Data.Entity.ModelConfiguration;using Portal.Entities;namespace Portal.Mapping {public class UserProfileMap : EntityTypeConfiguration<UserProfile>{public UserProfileMap(){// Primary Keythis.HasKey(t => t.UserID);// Propertiesthis.Property(t => t.UserID).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);this.Property(t => t.Name).IsRequired().HasMaxLength(50);this.Property(t => t.Email).IsRequired().HasMaxLength(100);this.Property(t => t.Telephone).HasMaxLength(50);this.Property(t => t.Mobilephone).HasMaxLength(20);this.Property(t => t.Address).HasMaxLength(200);// Table & Column Mappingsthis.ToTable("UserProfile");this.Property(t => t.UserID).HasColumnName("UserID");this.Property(t => t.Name).HasColumnName("Name");this.Property(t => t.Sex).HasColumnName("Sex");this.Property(t => t.Birthday).HasColumnName("Birthday");this.Property(t => t.Email).HasColumnName("Email");this.Property(t => t.Telephone).HasColumnName("Telephone");this.Property(t => t.Mobilephone).HasColumnName("Mobilephone");this.Property(t => t.Address).HasColumnName("Address");this.Property(t => t.CreateDate).HasColumnName("CreateDate");// Relationshipsthis.HasRequired(t => t.User).WithRequiredDependent(t => t.UserProfile);}} } View Code在以上實體類及實體映射類執行以后,生成的數據表結構如下:
在生成的表結構中,UserProfile表中的主鍵UserID同時也作為外鍵引用User表的主鍵UserID。若修改UserProfileMap.cs如下,則生成的表結構User表的主鍵UserID將作為你外鍵引用UserProfile表的主鍵UserID。
using System; using System.Collections.Generic; using System.Linq; using System.Text;using System.ComponentModel.DataAnnotations.Schema; using System.Data.Entity.ModelConfiguration;using Portal.Entities;namespace Portal.Mapping {public class UserProfileMap : EntityTypeConfiguration<UserProfile>{public UserProfileMap(){// Primary Keythis.HasKey(t => t.UserID);// Propertiesthis.Property(t => t.UserID).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);this.Property(t => t.Name).IsRequired().HasMaxLength(50);this.Property(t => t.Email).IsRequired().HasMaxLength(100);this.Property(t => t.Telephone).HasMaxLength(50);this.Property(t => t.Mobilephone).HasMaxLength(20);this.Property(t => t.Address).HasMaxLength(200);// Table & Column Mappingsthis.ToTable("UserProfile");this.Property(t => t.UserID).HasColumnName("UserID");this.Property(t => t.Name).HasColumnName("Name");this.Property(t => t.Sex).HasColumnName("Sex");this.Property(t => t.Birthday).HasColumnName("Birthday");this.Property(t => t.Email).HasColumnName("Email");this.Property(t => t.Telephone).HasColumnName("Telephone");this.Property(t => t.Mobilephone).HasColumnName("Mobilephone");this.Property(t => t.Address).HasColumnName("Address");this.Property(t => t.CreateDate).HasColumnName("CreateDate");// Relationshipsthis.HasRequired(t => t.User).WithRequiredPrincipal(t => t.UserProfile);}} } View Code4、多對多關系
Entity Framework Code First在根據定義的多對多關系的類生成數據表時,除了生成實體類定義的屬性表之外,還會生成一個中間表。用于體現兩個實體表之間的多對多的關系。示例實體類User與Role為多對多關系,一個用戶可以屬于多個角色,一個角色可以包含多個用戶。
文件類User.cs:
using System; using System.Collections.Generic;namespace Portal.Entities {public partial class User{public int UserID { get; set; }public string UserName { get; set; }public string Password { get; set; }public Nullable<bool> IsValid { get; set; }public virtual ICollection<Role> Roles { get; set; }} } View Code文件類Role.cs:
using System; using System.Collections.Generic;namespace Portal.Entities {public partial class Role{public Role(){this.Users = new List<User>();}public int RoleID { get; set; }public string RoleName { get; set; }public virtual ICollection<User> Users { get; set; }} } View Code在定義以上兩個類之后,不再添加任何的Entity Framework Code First與數據庫的映射配置,運行之后,生成的數據表結構為:
從以上的表結構中,可以看出,實體類運行之后,除了生成Users表和Roles表之外,還生成了RoleUsers表作為中介表,體現Users表和Roles表之間的多對多關聯關系。中介表RoleUsers的字段生成規則按照 [目標類型名稱]+[目標類型鍵名稱] 的約定。
Entity Framework Code First根據默認約定生成的多對多關聯關系的表時,默認啟用多對多的數據級聯刪除,可以添加代碼進行禁用。
protected override void OnModelCreating(DbModelBuilder modelBuilder) {// 禁用多對多關系表的級聯刪除modelBuilder.Conventions.Remove<ManyToManyCascadeDeleteConvention>(); }FluentAPI實現方式:
文件類User.cs:
using System; using System.Collections.Generic;namespace Portal.Entities {public partial class User{public int UserID { get; set; }public string UserName { get; set; }public string Password { get; set; }public Nullable<bool> IsValid { get; set; }public virtual ICollection<Role> Roles { get; set; }} } View Code映射文件類UserMap.cs:
using System.ComponentModel.DataAnnotations.Schema; using System.Data.Entity.ModelConfiguration;using Portal.Entities;namespace Portal.Mapping {public class UserMap : EntityTypeConfiguration<User>{public UserMap(){// Primary Keythis.HasKey(t => t.UserID);// Propertiesthis.Property(t => t.UserName).HasMaxLength(50);this.Property(t => t.Password).HasMaxLength(100);// Table & Column Mappingsthis.ToTable("User");this.Property(t => t.UserID).HasColumnName("UserID");this.Property(t => t.UserName).HasColumnName("UserName");this.Property(t => t.Password).HasColumnName("Password");this.Property(t => t.IsValid).HasColumnName("IsValid");}} } View Code文件類Role.cs:
using System; using System.Collections.Generic;namespace Portal.Entities {public partial class Role{public int RoleID { get; set; }public string RoleName { get; set; }public virtual ICollection<User> Users { get; set; }} } View Code映射文件類RoleMap.cs:
using System.ComponentModel.DataAnnotations.Schema; using System.Data.Entity.ModelConfiguration;using Portal.Entities;namespace Portal.Mapping {public class RoleMap : EntityTypeConfiguration<Role>{public RoleMap(){// Primary Keythis.HasKey(t => t.RoleID);// Propertiesthis.Property(t => t.RoleName).HasMaxLength(50);// Table & Column Mappingsthis.ToTable("Role");this.Property(t => t.RoleID).HasColumnName("RoleID");this.Property(t => t.RoleName).HasColumnName("RoleName");// Relationshipsthis.HasMany(t => t.Users).WithMany(t => t.Roles).Map(m =>{m.ToTable("UserRole");m.MapLeftKey("RoleID");m.MapRightKey("UserID");});}} } View Code運行之后生成的表結構:
5、一對多自反關系
一對多自反關系,即一個表存在一個外鍵列引用自身的主鍵。在項目中,最常見的一對多自反關系為分類表,分類表通過一個ParentID列保存引用主鍵,已實現無限級遞歸。
Fluent API實現方式:
文件類Category.cs:
using System; using System.Collections.Generic;namespace Portal.Entities {public class Category{public int CategoryID { get; set; }public int CategoryNo { get; set; }public string CategoryName { get; set; }public Nullable<int> ParentID { get; set; }public virtual Category Parent { get; set; }public virtual ICollection<Category> Children { get; set; }} } View Code映射文件類CategoryMap.cs:
using System.ComponentModel.DataAnnotations.Schema; using System.Data.Entity.ModelConfiguration;using Portal.Entities;namespace Portal.Mapping {public class CategoryMap : EntityTypeConfiguration<Category>{public CategoryMap(){// Primary Keythis.HasKey(t => t.CategoryID);// Propertiesthis.Property(t => t.CategoryName).IsRequired().HasMaxLength(50);// Table & Column Mappingsthis.ToTable("Category");this.Property(t => t.CategoryID).HasColumnName("CategoryID");this.Property(t => t.CategoryNo).HasColumnName("CategoryNo");this.Property(t => t.CategoryName).HasColumnName("CategoryName");this.Property(t => t.ParentID).HasColumnName("ParentID");// Relationshipsthis.HasOptional(t => t.Parent).WithMany(t => t.Children).HasForeignKey(d => d.ParentID);}} } View Code以上代碼在運行之后,生成的數據表:
6、多對多自反關系
多對多關系示例:Family中一條記錄可能有多個Parents,也可能有多個Children。
文件類Family.cs:
using System; using System.Collections.Generic;namespace Portal.Entities {/// <summary>/// Family表多對多自反關系/// </summary>public partial class Family{public Family(){this.Parents = new List<Family>();this.Children = new List<Family>();}public int FamilyID { get; set; }public string Name { get; set; }public Nullable<bool> Sex { get; set; }public Nullable<System.DateTime> Birthday { get; set; }public virtual ICollection<Family> Parents { get; set; }public virtual ICollection<Family> Children { get; set; }} } View Code映射文件類FamilyMap.cs:
using System.ComponentModel.DataAnnotations.Schema; using System.Data.Entity.ModelConfiguration;using Portal.Entities;namespace Portal.Mapping {public class FamilyMap : EntityTypeConfiguration<Family>{public FamilyMap(){// Primary Keythis.HasKey(t => t.FamilyID);// Propertiesthis.Property(t => t.Name).HasMaxLength(50);// Table & Column Mappingsthis.ToTable("Family");this.Property(t => t.FamilyID).HasColumnName("FamilyID");this.Property(t => t.Name).HasColumnName("Name");this.Property(t => t.Sex).HasColumnName("Sex");this.Property(t => t.Birthday).HasColumnName("Birthday");// Relationshipsthis.HasMany(t => t.Parents).WithMany(t => t.Children).Map(m =>{m.ToTable("FamilyRelationship");m.MapLeftKey("ParentID");m.MapRightKey("ChildID");});}} } View CodeFamily類及映射配置類,在運行之后生成的數據表結構:
在上面的表結構中,指定Family之間的中介表為FamilyRelationship,其中FamilyRelationship的兩個字段ParentID及ChildID均引用Familyi表中的FamilyID作為外鍵。可能在實際的項目過程中,出現這種多對多自反引用關系的情況比較少見。
總結
以上是生活随笔為你收集整理的Entity Framework Code First关系映射约定的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: BigMemroy系列文章--11. B
- 下一篇: BZOJ2888 : 资源运输