Entity Framework 7 (EF7)中的繼承對映允許您將類層次結構對映到資料庫中的表層次結構。具體而言,這意味著您可以建立一個基類,然後從該基類派生多個子類,並將這些子類對映到不同的資料庫表。這使得在資料庫中儲存不同型別的資料變得更加方便,同時還能保持物件導向程式設計的優雅性。
EF7提供了三種型別的繼承對映:單表繼承(Table Per Hierarchy,TPH)、分層表繼承(Table Per Type,TPT)和單獨的表繼承(Table Per Concrete Class,TPC)。下面分別介紹這三種對映型別。
效能基準
單表繼承對映(Table Per Hierarchy,TPH)
單表繼承對映將整個類層次結構對映到單個資料庫表中。每個類在表中對應一行,其中包括一個用於區分不同子類的Discriminator列。例如,考慮以下類層次結構:
public abstract class Animal
{
public int Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
}
public class Dog : Animal
{
public string Breed { get; set; }
}
public class Cat : Animal
{
public bool IsIndoor { get; set; }
}
使用單表繼承對映,可以將這個類層次結構對映到以下資料庫表:
+----+-------+-----+-------+------------+------------+
| Id | Name | Age | Breed | IsIndoor | Discriminator |
+----+-------+-----+-------+------------+------------+
| 1 | Fido | 3 | Poodle| NULL | Dog |
+----+-------+-----+-------+------------+------------+
| 2 | Felix | 5 | NULL | true | Cat |
+----+-------+-----+-------+------------+------------+
可以看到,每個Animal子類都在同一個表中表示為一行,並使用Discriminator列進行區分。
要使用TPH對映,在EF7中,可以使用Fluent API進行配置,如下所示:
modelBuilder.Entity<Animal>()
.HasDiscriminator<string>("Discriminator")
.HasValue<Dog>("Dog")
.HasValue<Cat>("Cat");
這個程式碼片段將Animal類設定為基類,然後使用HasDiscriminator方法指定Discriminator列的名稱。接下來,使用HasValue方法為每個Animal子類指定一個值,以便在Discriminator列中進行區分。
分層表繼承對映(Table Per Type,TPT)
分層表繼承對映將基類和每個子類對映到不同的資料庫表中。每個子類的表都包含基類的所有列和子類特有的列。例如,對於上述類層次結構,使用分層表繼承對映,可以將Animal、Dog和Cat分別對映到三個不同的表中:
Animal table:
+----+-------+-----+
| Id | Name | Age |
+----+-------+-----+
| 1 | Fido | 3 |
+----+-------+-----+
| 2 | Felix | 5 |
+----+-------+-----+
Dog table:
+----+-------+-----+-------+
| Id | Name | Age | Breed |
+----+-------+-----+-------+
| 1 | Fido | 3 | Poodle|
+----+-------+-----+-------+
Cat table:
+----+-------+-----+----------+
| Id | Name | Age | IsIndoor |
+----+-------+-----+----------+
| 2 | Felix | 5 | true |
+----+-------+-----+----------+
可以看到,每個表都只包含基類和對應的子類的列。每個子類表還包含一個外來鍵,指向基類表的主鍵。
要使用TPT對映,在EF7中,可以使用Fluent API進行配置,如下所示:
modelBuilder.Entity<Animal>()
.ToTable("Animals");
modelBuilder.Entity<Dog>()
.ToTable("Dogs")
.HasOne(a => a.Animal)
.WithOne()
.HasForeignKey<Dog>(d => d.Id);
modelBuilder.Entity<Cat>()
.ToTable("Cats")
.HasOne(a => a.Animal)
.WithOne()
.HasForeignKey<Cat>(c => c.Id);
這個程式碼片段中,Animal被對映到Animals表中。對於每個Animal子類,使用ToTable方法將其對映到相應的表中。在每個子類表中,使用HasOne方法指定一個導航屬性,表示它與基類表中的一行相關聯。使用HasForeignKey方法指定外來鍵屬性和基類表中的主鍵。
單獨的表繼承對映(Table Per Concrete Class,TPC)
單獨的表繼承對映將每個類都對映到一個獨立的資料庫表中。每個表都只包含該類的列。這意味著基類和子類之間沒有直接的關係。例如,對於上述類層次結構,使用單獨的表繼承對映,可以將Animal、Dog和Cat分別對映到三個不同的表中:
Animal table:
+----+-------+-----+
| Id | Name | Age |
+----+-------+-----+
| 1 | Fido | 3 |
+----+-------+-----+
| 2 | Felix | 5 |
+----+-------+-----+
Dog table:
+----+-------+-----+-------+
| Id | Name | Age | Breed |
+----+-------+-----+-------+
| 1 | Fido | 3 | Poodle|
+----+-------+-----+-------+
Cat table:
+----+-------+-----+----------+
| Id | Name | Age | IsIndoor |
+----+-------+-----+----------+
| 2 | Felix | 5 | true |
+----+-------+-----+----------+
要使用TPC對映,在EF7中,可以使用Fluent API進行配置,如下所示:
modelBuilder.Entity<Animal>()
.ToTable("Animals");
modelBuilder.Entity<Dog>()
.ToTable("Dogs");
modelBuilder.Entity<Cat>()
.ToTable("Cats");
這個程式碼片段中,每個類都被對映到不同的表中,使用ToTable方法指定表名。
繼承對映的查詢
在EF7中,使用繼承對映時,可以查詢基類和子類的例項。例如,以下程式碼查詢所有動物的名字:
using (var context = new AnimalContext())
{
var names = context.Set<Animal>()
.Select(a => a.Name)
.ToList();
}
這個查詢將返回Animal表和所有子類表中的所有名稱。如果要只查詢特定型別的動物,可以在查詢中使用OfType方法,例如:
using (var context = new AnimalContext())
{
var dogNames = context.Set<Animal>()
.OfType<Dog>()
.Select(d => d.Name)
.ToList();
}
這個查詢只返回Dog表中的名稱。
總結
繼承對映是一種重要的ORM技術,允許將類層次結構對映到資料庫表層次結構中。EF7支援三種繼承對映策略:分層表繼承對映、單獨的表繼承對映和聯合表繼承對映。使用Fluent API可以很容易地配置這些對映。在查詢資料時,可以使用OfType方法篩選出特定型別的例項。