简体   繁体   English

实体框架:TPC MapInheritedProperties模型超类属性

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

Summary : in Entity Framework I use TPC to create two classes derived from the same base class. 摘要 :在Entity Framework中,我使用TPC创建从同一基类派生的两个类。 In fluent API I map inherited properties, but how to model the properties of the base class? 在流畅的API中,我映射了继承的属性,但是如何对基类的属性建模?

More extensive description In Entity Framework I have a class Child, and two kinds of Children: a Boy and a Girl. 更广泛的描述在Entity Framework中,我有一个类Child,和两种孩子:Boy和Girl。 Both Boy and Girl derive from 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;}
}

I want a table with boys and a table with girls, each table also having the Child properties. 我想要一个带有男孩的表和一个带有女孩的表,每个表还具有Child属性。

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

From several sources, for example this one I learned that this is called TPC: table per concrete class and that I should MapInheritedProperties in OnModelCreating 从几个来源,例如,我了解到这被称为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);
}

During SaveChanges I get an InvlidOperationException indicating that the primary key is not unique. 在SaveChanges期间,我收到一个InvlidOperationException异常,指示主键不是唯一的。 Removing the part that builds Child solves this problem. 删除构建Child的部件可以解决此问题。

How to build the Child properties without having to do this in the Girl and again in the Boy properties? 如何构建子级属性,而不必在“女孩”属性和“男孩”属性中再次执行此操作?

SHORT ANSWER: 简短答案:

If you want your code to work, remove any reference to Child entity in your model configuration. 如果你希望你的代码工作,删除任何引用Child模型中的配置实体。 As soon as EF knows about Child as Entity it will enforce the following rule: There cannot be 2 entities of type Child or 2 entities that inherit from Child with the same PK in memory. 只要EF知道Child的实体将执行以下规则:不能有型的2个实体Child或2个实体继承Child与内存中的同PK。 You can see the error tells you that the entities where successfully persisted; 您可以看到错误告诉您实体成功保存的位置; but when EF pulls the new IDs it finds out both have the same ID. 但是当EF拉出新ID时,它发现两者具有相同的ID。

LONG ANSWER 长答案

Remove 去掉

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

Instead this is how your OnModelCreating method should look like. 相反,这就是您的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);
}

If you don't want the configuration repetition in the configuration I would use the DataAnnotations attributes on the base class to enforce the name to be required. 如果您不希望在配置中重复配置,则可以在基类上使用DataAnnotations属性来强制使用必需的名称。

You will also need to enforce that the Id property to be auto-generated in database. 您还需要强制在数据库中自动生成Id属性。 This doesn't happen by convention when using the Map method in the fluent API. 在流利的API中使用Map方法时,按惯例不会发生这种情况。 You can see I added the fluent calls to make that happen in both the Girl and Boy mapping. 您可以看到我添加了流利的调用,以实现在“ Girl和“ Boy贴图中的实现。

Hope this helps. 希望这可以帮助。

I reworked Arturo's solution proposal. 我重新设计了Arturo的解决方案。 This solution was too long to describe as a comment. 此解决方案太长,无法描述为评论。 So Arturo: thanks for giving me the ideas . 所以Arturo:感谢您给我的想法 Chapeau! 起首!

Arturo suggested to use Data Annotations. Arturo建议使用数据注释。 The reason that I don't want to use that method is, that the modelling of the class doesn't necessary correspond with a certain database representation. 我不想使用该方法的原因是,该类的建模不必与某个数据库表示相对应。 I bit hypothetical, but if I want a smaller maximum length for a boy's Name than for a Girl's name, then a data annotation wouldn't help. 我有点假设,但是如果我希望男孩名的最大长度小于女孩名的最大长度,那么数据注释将无济于事。

Besides, there are some things that has to be done using fluent API. 此外,使用流利的API还需要做一些事情。 For instance, you can't say that a System.DateTime has a DateTime2 format in the database using DataAnnotations. 例如,您不能说使用DataAnnotations在数据库中System.DateTime具有DateTime2格式。

If you didn't guess it already: my problem description was highly simplified. 如果您还没有猜到:我的问题描述得到了高度简化。 All three classes have a lot of properties that need a lot of fluent API configurations 这三个类都有很多属性,需要大量流利的API配置

Arturo's remarks helped me to the following solution: 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)...
    }
}

And in MyDbContext: 在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