簡體   English   中英

實體框架:TPC MapInheritedProperties模型超類屬性

[英]Entity Framework: TPC MapInheritedProperties model super class properties

摘要 :在Entity Framework中,我使用TPC創建從同一基類派生的兩個類。 在流暢的API中,我映射了繼承的屬性,但是如何對基類的屬性建模?

更廣泛的描述在Entity Framework中,我有一個類Child,和兩種孩子:Boy和Girl。 男孩和女孩都來自兒童:

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;}
}

我想要一個帶有男孩的表和一個帶有女孩的表,每個表還具有Child屬性。

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

從幾個來源,例如,我了解到這被稱為TPC:每個具體類的表,我應該在OnModelCreating中使用MapInheritedProperties

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異常,指示主鍵不是唯一的。 刪除構建Child的部件可以解決此問題。

如何構建子級屬性,而不必在“女孩”屬性和“男孩”屬性中再次執行此操作?

簡短答案:

如果你希望你的代碼工作,刪除任何引用Child模型中的配置實體。 只要EF知道Child的實體將執行以下規則:不能有型的2個實體Child或2個實體繼承Child與內存中的同PK。 您可以看到錯誤告訴您實體成功保存的位置; 但是當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方法時,按慣例不會發生這種情況。 您可以看到我添加了流利的調用,以實現在“ Girl和“ Boy貼圖中的實現。

希望這可以幫助。

我重新設計了Arturo的解決方案。 此解決方案太長,無法描述為評論。 所以Arturo:感謝您給我的想法 起首!

Arturo建議使用數據注釋。 我不想使用該方法的原因是,該類的建模不必與某個數據庫表示相對應。 我有點假設,但是如果我希望男孩名的最大長度小於女孩名的最大長度,那么數據注釋將無濟於事。

此外,使用流利的API還需要做一些事情。 例如,您不能說使用DataAnnotations在數據庫中System.DateTime具有DateTime2格式。

如果您還沒有猜到:我的問題描述得到了高度簡化。 這三個類都有很多屬性,需要大量流利的API配置

Arturo的言論幫助我實現了以下解決方案:

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(...));
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM