[英]Table Per Concrete Type (TPC) Inheritance in Entity Framework 6 (EF6)
為了避免使用Table Per Hierarchy(TPH),我一直在研究如何在我的數據庫模型中最好地實現Table-Per-Concrete Class(TPC)繼承。 我遇到了官方文檔和本文 。
下面是一些帶有一些簡單繼承的模型類。
public class BaseEntity
{
public BaseEntity()
{
ModifiedDateTime = DateTime.Now;
}
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public DateTime ModifiedDateTime { get; set; }
}
public class Person : BaseEntity
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class Business : BaseEntity
{
public string Name { get; set; }
public string Location { get; set; }
}
根據兩篇文章中的示例使用的DbModelBuilder配置。
modelBuilder.Entity<BaseEntity>()
.Property(c => c.Id)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
modelBuilder.Entity<Person>().Map(m =>
{
m.MapInheritedProperties();
m.ToTable("Person");
});
modelBuilder.Entity<Business>().Map(m =>
{
m.MapInheritedProperties();
m.ToTable("Business");
});
應用程序運行成功,但當我返回數據庫時,我找到三(3)個表而不是我期望找到的兩個(2)表。 經過一些測試后,似乎會創建“BaseEntity”表,但從未使用過。 除了這個空的孤立表外,一切似乎都運行得很好。
我搞亂了DbModelBuilder配置,最終刪除了提供預期結果的“BaseEntity”配置; 兩(2)個表,每個表都具有正確的屬性並且功能正常。
我做最后一次測試,刪除所有DbModelBuilder配置,只包括“Person”和“Business”的兩(2)個DbSet屬性並再次測試。
public DbSet<Person> People { get; set; }
public DbSet<Business> Businesses { get; set; }
令我驚訝的是,項目構建,轉到數據庫,只創建具有所有類屬性的兩個表,包括來自“BaseEntity”類的繼承屬性。 我可以毫無問題地進行CRUD操作。
在運行了許多測試后,我發現最終測試沒有任何問題,而且我無法重現這兩篇文章所警告的重復鍵錯誤。
已成功提交對數據庫的更改,但更新對象上下文時發生錯誤。 ObjectContext可能處於不一致狀態。 內部異常消息:AcceptChanges無法繼續,因為對象的鍵值與ObjectStateManager中的另一個對象沖突。 在調用AcceptChanges之前,請確保鍵值是唯一的。
為了簡化這一點,我已經移動了強制TablePerConcrete開源的必要代碼。 其目的是允許通常僅在Fluent界面中提供的功能(您必須將大量代碼分散到Db類的OnModelCreating方法中)才能遷移到基於屬性的功能。
它允許你做這樣的事情:
[TablePerConcrete]
public class MySubclassTable : MyParentClassEntity
強制TPC,無論EF可能決定從您的父類/子類關系推斷出什么。
這里一個有趣的挑戰是,有時EF會搞砸一個繼承的Id屬性,將其設置為填充顯式值而不是數據庫生成。 你可以通過讓父類實現接口IId
(它只是說:它有一個Id屬性)來確保不這樣做,然后使用[ForcePKId]
標記子類。
public class MyParentClassEntity : IId
{
public int Id { get; set; }
. . .
[TablePerConcrete]
[ForcePKId]
public class MySubclassTable : MyParentClassEntity
{
// No need for PK/Id property here, it was inherited and will work as
// you intended.
開始為您處理所有這些的代碼非常簡單 - 只需在您的Db類中添加幾行:
public class Db : DbContext
{
. . .
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
var modelsProject = Assembly.GetExecutingAssembly();
B9DbExtender.New().Extend(modelBuilder, modelsProject);
您可以通過以下兩種方式之一訪問它:
通過一個要點將所有相關類復制粘貼到一個文件中,在這里: https : //gist.github.com/b9chris/8efd30687d554d1ceeb3fee359c179f9
通過庫,我們的Brass9.Data,我們正在發布開源。 它還有很多其他的EF6工具,比如Data Migrations。 它也更有條理,按照您通常的預期將類拆分為單獨的文件: https : //github.com/b9chris/Brass9.Data
我使用映射類,但從不介意。 我像這樣解決它:
public class PersonMap : EntityTypeConfiguration<Person>
{
public PersonMap()
{
Map(m => { m.ToTable("Person"); m.MapInheritedProperties(); });
HasKey(p => p.Id);
Property(p => p.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
}
}
記住 - 基類必須是抽象的。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.