[Entity Framework][Code First]Mapping Inheritance Hierarchies

TPH vs TPT vs TPC

前言

Entity Framework 支援三種繼承的Model設計,Table Per Hierarchy(TPH)、Table Per Type(TPT)、Table Per Concrete Class(TPC),每一種設計各有其優缺點,而今天就來介紹這三者的差異。

 

Table Per Hierarchy(TPH)

Code First預設就是採用TPH策略,mapping 繼承的型別(子類別的欄位)至單一的資料表,該資料表會有一個Discriminator column來區分是哪一個子類別。

例如以下範例:

public class Person
{
    public int Id { get; set; }
    public string Name { get; set; }
}

public class Teacher : Person
{
    public string Position { get; set; }
}

會產生這樣的Table

而產生的Discriminator會來記錄是用Person新增的資料還是Teacher新增的資料

自訂Discriminator

預設的Discriminator如果不喜歡,可用Fluent API自訂該欄位型別或者欄位名稱

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Person>()
        .HasDiscriminator<bool>("IsTeacher")
        .HasValue<Person>(false)
        .HasValue<Teacher>(true);
}

重新產生後的欄位

 

Table Per Type(TPT)

TPT只會儲存base class 的properties至單一table,而子類別的欄位會存在不同的table,並且用foreign key關聯。

設定方法則是將子類別指定table名稱就可以了

public class Person
{
    public int Id { get; set; }
    public string Name { get; set; }
}

[Table("Teacher")]
public class Teacher : Person
{
    public string Position { get; set; }
}

或者使用Fluent API的方式

modelBuilder.Entity<Person>()
            .Map<Teacher>(m =>
            {
                m.ToTable("Teacher");
            });

產生的Table

 

Table Per Concrete Class(TPC)

TPC並沒有base class的table,每一個子類別所產生的table都會包含base class的欄位,這個適合用在不同table中紀錄歷史資料。

使用Fluent API設定。  這邊只要是使用MapInheritedProperties()來告訴Code First要將父類別的Properites產生到子類別的table欄位上。

modelBuilder.Entity<Person>().Map(m =>
            {
                m.ToTable("Person");
            }).Map<Teacher>(m =>
            {
                m.ToTable("Teacher");
                m.MapInheritedProperties();
            });

產生的資料表

注意:TPC並不會建立foreign key關聯

 

小結

這三種方式各有優存點,詳細說明,可參考 How to choose an Inheritance Strategy

 

 

 

一天一分享,身體好健康。

該追究的不是過去的原因,而是現在的目的。