Entity Framework Code First關係映射約定

  本篇隨筆目錄:數據庫

  一、外鍵列名默認約定app

  二、一對多關係ide

  三、一對一關係測試

  四、多對多關係ui

  五、一對多自反關係this

  六、多對多自反關係spa

 

  在關係數據庫中,不一樣表之間每每不是所有都單獨存在,而是相互存在關聯的。兩個不一樣表之間能夠存在外鍵依賴關係,一個表自身也能夠有自反關係(表中的一個字段引用主鍵,從而也是外鍵字段)。翻譯

  Entity Framework Code First默認多重關係的一些約定規則:3d

  一對多關係:兩個類中分別包含一個引用和一個集合屬性,也能夠是一個類包含另外一個類的引用屬性,或一個類包含另外一個類的集合屬性。如在本篇接下來用到的例子Category類和Product類,要使得Category與Product之間具備一對多關係,Entity Framework Code First能夠有3種體現方式:code

  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處理一個表及多個表之間的關係。

  一、外鍵列名默認約定

  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。

  二、一對多關係

  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 Key
            this.HasKey(t => t.CategoryID);

            // Properties
            this.Property(t => t.CategoryName)
                .IsRequired()
                .HasMaxLength(50);

            // Table & Column Mappings
            this.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 Key
            this.HasKey(t => t.ProductID);

            // Properties
            this.Property(t => t.ProductName)
                .IsRequired()
                .HasMaxLength(100);

            // Table & Column Mappings
            this.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");

            // Relationships
            this.HasRequired(t => t.Category)
                .WithMany(t => t.Products)
                .HasForeignKey(d => d.CategoryID);
        }
    }
}
View Code

  PortalContext.cs的OnModelCreating方法:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Configurations.Add(new CategoryMap());
    modelBuilder.Configurations.Add(new ProductMap());
}
View Code

  三、一對一關係

   在一對一關係中,兩個表均有各自的主鍵,但要看哪一個表的主鍵同時做爲外鍵引用另外一個表的主鍵。示例以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 Key
            this.HasKey(t => t.UserID);

            // Properties
            this.Property(t => t.UserName)
                .HasMaxLength(50);

            this.Property(t => t.Password)
                .HasMaxLength(100);

            // Table & Column Mappings
            this.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 Key
            this.HasKey(t => t.UserID);

            // Properties
            this.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 Mappings
            this.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");

            // Relationships
            this.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 Key
            this.HasKey(t => t.UserID);

            // Properties
            this.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 Mappings
            this.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");

            // Relationships
            this.HasRequired(t => t.User)
                .WithRequiredPrincipal(t => t.UserProfile);
        }
    }
}
View Code

  四、多對多關係

  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 Key
            this.HasKey(t => t.UserID);

            // Properties
            this.Property(t => t.UserName)
                .HasMaxLength(50);

            this.Property(t => t.Password)
                .HasMaxLength(100);

            // Table & Column Mappings
            this.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 Key
            this.HasKey(t => t.RoleID);

            // Properties
            this.Property(t => t.RoleName)
                .HasMaxLength(50);

            // Table & Column Mappings
            this.ToTable("Role");
            this.Property(t => t.RoleID).HasColumnName("RoleID");
            this.Property(t => t.RoleName).HasColumnName("RoleName");

            // Relationships
            this.HasMany(t => t.Users)
                .WithMany(t => t.Roles)
                .Map(m =>
                    {
                        m.ToTable("UserRole");
                        m.MapLeftKey("RoleID");
                        m.MapRightKey("UserID");
                    });
        }
    }
}
View Code

  運行以後生成的表結構:

  五、一對多自反關係

  一對多自反關係,即一個表存在一個外鍵列引用自身的主鍵。在項目中,最多見的一對多自反關係爲分類表,分類表經過一個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 Key
            this.HasKey(t => t.CategoryID);

            // Properties
            this.Property(t => t.CategoryName)
                .IsRequired()
                .HasMaxLength(50);

            // Table & Column Mappings
            this.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");

            // Relationships
            this.HasOptional(t => t.Parent)
                .WithMany(t => t.Children)
                .HasForeignKey(d => d.ParentID);
        }
    }
}
View Code

  以上代碼在運行以後,生成的數據表:

  六、多對多自反關係

  多對多關係示例: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 Key
            this.HasKey(t => t.FamilyID);

            // Properties
            this.Property(t => t.Name)
                .HasMaxLength(50);

            // Table & Column Mappings
            this.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");

            // Relationships
            this.HasMany(t => t.Parents)
                .WithMany(t => t.Children)
                .Map(m =>
                    {
                        m.ToTable("FamilyRelationship");
                        m.MapLeftKey("ParentID");
                        m.MapRightKey("ChildID");
                    });
        }
    }
}
View Code

  Family類及映射配置類,在運行以後生成的數據表結構:

  在上面的表結構中,指定Family之間的中介表爲FamilyRelationship,其中FamilyRelationship的兩個字段ParentID及ChildID均引用Familyi表中的FamilyID做爲外鍵。可能在實際的項目過程當中,出現這種多對多自反引用關係的狀況比較少見。

相關文章
相關標籤/搜索