【问题标题】:Entity Framework: TPC MapInheritedProperties model super class properties实体框架:TPC MapInheritedProperties 模型超类属性
【发布时间】:2016-11-16 15:00:16
【问题描述】:

总结:在 Entity Framework 中,我使用 TPC 创建了两个派生自同一个基类的类。在fluent API中我映射继承的属性,但是如何对基类的属性进行建模?

更广泛的描述 在实体框架中,我有一个类 Child 和两种孩子:一个男孩和一个女孩。 Boy 和 Girl 都派生自 Child:

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

From several sources, for example this one 我知道这叫做 TPC: table per specific class,我应该在 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 属性而不必在 Girl 和 Boy 属性中执行此操作?

【问题讨论】:

  • 你的Child类是你真实模型中的抽象吗?
  • Child 摘要:我不打算创建 Child 对象,只创建 Boys 和 Girls。我也不想创建子表。因此,如果我可以帮助使 Child 抽象化,那么我会这样做。我当然可以让一个男孩成为一个孩子的组合,这样可以解决问题,但对我来说,男孩有一个孩子而不是一个孩子对我来说似乎有点奇怪

标签: c# entity-framework inheritance ef-fluent-api


【解决方案1】:

简短回答:

如果您希望代码正常工作,请删除模型配置中对 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 属性。在 fluent API 中使用 Map 方法时,这不会按照惯例发生。你可以看到我在GirlBoy 映射中添加了流畅的调用来实现这一点。

希望这会有所帮助。

【讨论】:

    【解决方案2】:

    我重新设计了 Arturo 的解决方案建议。这个解决方案太长了 描述为评论。所以Arturo:谢谢你给我这些想法。 起首!

    Arturo 建议使用数据注释。我不想使用该方法的原因是,类的建模不必与特定的数据库表示相对应。我有点假设,但如果我希望男孩姓名的最大长度小于女孩姓名的最大长度,那么数据注释将无济于事。

    此外,还有一些事情必须使用 fluent 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(...));
    }
    

    【讨论】:

    • 配置类的优秀使用。
    猜你喜欢
    • 2016-03-26
    • 1970-01-01
    • 2011-02-14
    • 1970-01-01
    • 1970-01-01
    • 2016-10-28
    • 2017-07-20
    • 2015-12-27
    • 1970-01-01
    相关资源
    最近更新 更多