2016-11-16 64 views
1

摘要:在實體框架中我使用TPC創建兩個派生自相同基類的類。在流利的API中,我映射了繼承的屬性,但是如何建模基類的屬性?實體框架:TPC MapInheritedProperties模型超類屬性

更詳細的描述 在實體框架中我有一個類Child和兩種Children:一個男孩和一個女孩。男孩和女孩都來自孩子:

public class Child 
{ 
    public int Id {get; set;} 
    public string Name {get; set;} 
} 
public class Boy : Child 
{ 
    public string SomeBoyishProperty {get; set;} 
} 
public class Girl : Child 
{ 
    public string SomeGirlyProperty {get; set;} 
} 

我想要一個男孩和女孩的桌子,每張桌子也有孩子的屬性。

public class MyDbContext : DbContext 
{ 
    public DbSet<Boy> Boys {get; set;} 
    public DbSet<Girl> Girls {get; set; 
} 

From several sources, for example this one我才知道,這就是所謂的TPC:每個具體類,我應該MapInheritedProperties在OnModelCreating

protected override void OnModelCreating(DbModelBuilder modelBuilder) 
{ 
    base.OnModelCreating(modelBuilder); 

    // model the properties of the base class, for instance set max length 
    modelBuilder.Entity<Child>() 
     .Property(p => p.Name).IsRequired().HasMaxLength(12); 

    // Model Daughter: 
    modelBuilder.Entity<Daughter>() 
    .Map(m => 
    { 
     m.MapInheritedProperties(); 
     m.ToTable("Daughters"); 
    }) 
    .Property(p => p.SomeGirlyProperty).IsOptional().HasMaxLength(13); 

    // model Boy 
    modelBuilder.Entity<Son>() 
    .Map(m => 
    { 
     m.MapInheritedProperties(); 
     m.ToTable("Sons"); 
    }) 
    .Property(p => p.SomeBoyishProperty).IsOptional().HasMaxLength(14); 
} 

表的SaveChanges過程中,我得到一個InvlidOperationException表明該主鍵不唯一。刪除構建孩子的部分解決了這個問題。

如何建立孩子屬性,而不必在女孩和男孩屬性中做到這一點?

+0

是你的'兒童'在你的真實模型中抽象類? –

+0

孩子摘要:我不打算創建兒童對象,只有男孩和女孩。我也不想創建一個Child表。所以如果我能幫助讓孩子抽象化,那麼我會這樣做。我當然可以讓一個男孩成爲一個孩子,這可以解決問題,但是對於我來說,一個男孩有一個孩子而不是一個孩子似乎有點奇怪 –

回答

2

簡短的回答:

如果你希望你的代碼工作,消除任何參考Child實體模型中的配置。只要EF知道Child作爲實體,它將執行以下規則:不能有2個Child類型的實體或2個實體從內存中具有相同的PK繼承Child。你可以看到這個錯誤告訴你成功持久的實體;但是當EF拉取新的ID時,發現它們都具有相同的ID。

長的答案

刪除

modelBuilder.Entity<Child>() 
    .Property(p => p.Name).IsRequired().HasMaxLength(12); 

這實際上是你的OnModelCreating方法應該是什麼樣子等。

protected override void OnModelCreating(DbModelBuilder modelBuilder) 
{ 
    base.OnModelCreating(modelBuilder); 

    // Model Daughter: 
    var girlEntity = modelBuilder.Entity<Girl>(); 
    girlEntity.Map(m => 
    { 
     m.MapInheritedProperties(); 
     m.ToTable("Daughters"); 
    }); 
    girlEntity.Property(p => p.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity); 
    girlEntity.Property(p => p.Name).IsRequired().HasMaxLength(12); 
    girlEntity.Property(p => p.SomeGirlyProperty).IsOptional().HasMaxLength(13); 

    // model Boy 
    var boyEntity = modelBuilder.Entity<Boy>(); 
    boyEntity.Map(m => 
    { 
     m.MapInheritedProperties(); 
     m.ToTable("Sons"); 
    }); 
    boyEntity.Property(p => p.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity); 
    boyEntity.Property(p => p.Name).IsRequired().HasMaxLength(12); 
    boyEntity.Property(p => p.SomeBoyishProperty).IsOptional().HasMaxLength(14); 
} 

如果你不想在配置配置重複我會用在基類中的DataAnnotations屬性執行名稱是必需的。

您還需要強制執行Id屬性以在數據庫中自動生成。在流利的API中使用Map方法時,這不會按慣例發生。你可以看到我添加了流暢的調用,使其在GirlBoy映射中都發生。

希望這會有所幫助。

2

我重新修改了Arturo的解決方案。這個解決方案太長,以 描述爲評論。所以阿圖羅:感謝給我的想法。 起首!

Arturo建議使用數據註釋。我不想使用這種方法的原因是,類的建模不需要與某個數據庫表示相對應。我有點假設,但如果我想要一個男孩的名字的最大長度小於女孩的名字,那麼數據註釋將無濟於事。

此外,還有一些事情必須使用流利的API來完成。例如,您不能說使用DataAnnotations在數據庫中System.DateTime格式爲DateTime2

如果您還沒有猜到它:我的問題描述是高度簡化的。所有這三個班有很多需要大量精通API配置

阿圖羅的一番話讓我到下面的解決方案的特性:

internal class ChildConfig<T> : EntityTypeConfiguration<T> where T : Child 
{ 
    public ChildConfig(...) 
    { 
     // configure all Child properties 
     this.Property(p => p.Name).... 
    } 
} 
internal class BoyConfig : ChildConfig<Boy> 
{ 
    public BoyConfig(...) : base (...) 
    { 
     // the base class will configure the Child properties 
     // configure the Boy properties here 
     this.Property(p => p.SomeBoyishProperty)... 
    } 
} 

而且在MyDbContext:

protected override void OnModelCreating(DbModelBuilder modelBuilder) 
{ 
    modelBuilder.Configurations.Add(new BoyConfig(...)); 
    modelBuilder.Configuration.Add(new GirlConfig(...)); 
} 
+0

極好的使用配置類。 –