实体框架+多级继承+EF代码优先

Posted

技术标签:

【中文标题】实体框架+多级继承+EF代码优先【英文标题】:Entity Framework + Multilevel Inheritance + EF Code first 【发布时间】:2011-07-27 15:52:45 【问题描述】:

我正在尝试使用 Code First 设置 TPC 继承。我有一个三级层次结构。 抽象类A,具体类B继承自A,类C继承自B。 A 类属性:ID、CreatedBy 和 CreatedOn。 B 类属性:FirstName、LastName、BirthDate C 类属性:约会日期、状态

我希望在数据库中创建具有由数据库生成的标识的 B 类和 C 类表。

我的上下文类中有以下代码:

public DbSet<B> BList  get; set; 
public DbSet<C> CList  get; set; 

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

        modelBuilder.Entity<B>().ToTable("BClass");
        modelBuilder.Entity<C>().ToTable("CClass");
    

我有一个 contextinitializer 类,它覆盖 Seed 方法以在数据库中填充一些测试数据。

protected override void Seed(TestContext context)
    
        TestDataClass testData = new TestDataClass();

        List<B> bList= testData.GetBList();

        foreach (B objB in bList)
        
            context.BList.Add(objB);
        

        List<C> cList = testData.GetCList();
        foreach (C objC in cList)
        
            context.CList.Add(objC);
        

        context.SaveChanges();
    

这会在数据库中创建表 BClass 和 CClass 但问题是如果我在 BClass 中插入 10 行并在 CClass 中插入相应的 10 行,实体框架将在 BClass 中插入 20 行,其中 10 行具有实际值,10 行具有 Nulls . CClass 给出了 10 行的预期结果。 第二个问题是当我从 CClass 查询数据时我没有得到结果。我的错误信息说:

EntitySet 'CClass' 未在 EntityContainer 中定义 '测试上下文'。靠近简单标识符,第 1 行,第 12 列。

我正在使用来自 http://huyrua.wordpress.com/2011/04/13/entity-framework-4-poco-repository-and-specification-pattern-upgraded-to-ef-4-1/ 的 Huy Nguyen 的 Entity Framework 4 POCO、存储库和规范模式 [升级到 EF 4.1] 帖子中的代码,我在通用存储库中的代码看起来像

public IList<TEntity> FindAll<TEntity>() where TEntity : class
    
        DbContext.Set<TEntity>();
        var entityName = GetEntityName<TEntity>();
        return ((IObjectContextAdapter)DbContext).ObjectContext.CreateQuery<TEntity>(entityName).ToList<TEntity>();
    

        private string GetEntityName<TEntity>() where TEntity : class
    
        DbContext.Set<TEntity>();
        return string.Format("0.1", ((IObjectContextAdapter)DbContext).ObjectContext.DefaultContainerName, _pluralizer.Pluralize(typeof(TEntity).Name));
    

我尝试更改表映射,但没有帮助。我不知道我在这里缺少什么,也不知道该怎么做。我急需帮助。

提前致谢。


这就是我的实际课程的样子:

public abstract class EntityBase

    [DatabaseGenerated(System.ComponentModel.DataAnnotations.DatabaseGeneratedOption.Identity)]
    public virtual long Id  get; set; 
    public virtual string CreatedBy  get; set; 
    public virtual DateTime? CreatedOn  get; set; 
    public virtual string LastModifiedBy  get; set; 
    public virtual DateTime? LastModifiedOn  get; set; 




public class Person: EntityBase

    public virtual string FirstName  get; set; 
public virtual string MiddleName  get; set; 
    public virtual string LastName  get; set; 
    public virtual DateTime? BirthDate  get; set; 


    [NotMapped]
    public virtual string FullName
    
        get
        
            return String.Format("0, 1 2", LastName, FirstName, MiddleName);
        
    


public partial class Employee : Person

[Required]
    public string EmployeeNumber  get; set; 
[Required]
public string department  get; set; 

    public  DateTime? JoiningDate  get; set; 
    public  DateTime? PromotionDate  get; set; 
 

我的上下文类现在有:

public DbSet<EntityBase> People  get; set; 

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

        modelBuilder.Entity<Person>().ToTable("Person");
        modelBuilder.Entity<Employee>().Map(m => m.MapInheritedProperties()).ToTable("Employee"); 

【问题讨论】:

【参考方案1】:

您的映射指定 TPT 而不是 TPC。您必须使用:

protected override void OnModelCreating(ModelBuilder modelBuilder)

    base.OnModelCreating(modelBuilder);

    modelBuilder.Entity<B>().ToTable("BClass"); 
    modelBuilder.Entity<C>().Map(m => m.MapInheritedProperties()).ToTable("CClass");

我希望在数据库中创建 B 类和 C 类的表 具有由数据库生成的身份。

EF 不会为您执行此操作。这将需要对生成的代码进行自定义修改,因为一旦使用继承实体必须在整个继承层次结构中的所有实例中具有唯一的 Id = id 在 B 和 C 表之间必须是唯一的。这可以通过使用种子 0 和增量 2 为 B 和种子 1 和增量 2 为 C 指定标识来轻松完成,但每次添加新的子实体类型时都必须修改它。

您的存储库很糟糕。为什么在使用 DbContext 时要转换回 ObjectContext?阅读有关使用和查询 DbContext 的内容,并在没有存储库的情况下开始。 ObjectContext API 也不允许在查询中使用派生类型。您必须查询***映射实体类型并使用OfType 扩展方法来仅获取派生类型。

var cs = objectContext.CreateSet<B>().OfType<C>().ToList();

这只是通用方法不起作用的另一个示例。

编辑:

对不起,我知道问题出在哪里。我的描述要快,我跳过了解决方案的一个重要细节。我没有映射 A (EntityBase),因为它不是必需的。它被标记为抽象实体,因此不需要为在 TPC 继承中始终为空的表提供单独的表。这也是我的 B (Person) 在其映射中不调用 MapInheritedProperties 的原因,因为我将它作为映射继承的根。因此,我为示例定义的 DbSet 必须定义为:

public DbSet<Person> People  get; set; 

如果你想定义DbSet&lt;EntityBase&gt;,你必须像Employee一样映射Person

modelBuilder.Entity<Person>().Map(m => m.MapInheritedProperties()).ToTable("Person"); 

您当前的映射将 EntityBasePerson 定义为 TPT 继承的一部分,将 Employee 定义为 TPC 继承,但 EF 不喜欢组合继承类型。

【讨论】:

感谢您的回复,拉迪斯拉夫。但现在得到一个不同的错误。我做的第一件事是根据上面的代码为 B 类和 C 类设置映射。错误是“C”类型无法按定义映射,因为它映射了使用实体拆分或其他形式继承的类型的继承属性。要么选择不同的继承映射策略以便不映射继承的属性,要么更改层次结构中的所有类型以映射继承的属性并且不使用拆分。我知道我必须阅读很多关于 EF、代码优先等的内容,但我需要解决这个问题并尽快让它运行。 你能给我指出一个涵盖确切场景的链接/示例吗?一旦上述问题得到解决,我将着手重构我的存储库。谢谢 您可能应该发布您的实体的代码,因为必须有一些细节破坏了映射,否则我给您指出了错误的方向。 请看我上面的实际实体。 我知道在 person 上有 MapInheritedProperties() 以便它在 Person 表中映射来自 EntityBase 的属性。但是,在 Employee 上使用 MapInheritedProperties() 会复制 Employee 中 Person 表中的列。如果我没有在 Employee 上指定 MapInheritedProperties(),那么它又会变成 TPC 和 TPT 的组合。对吗?

以上是关于实体框架+多级继承+EF代码优先的主要内容,如果未能解决你的问题,请参考以下文章

用于一对零或一关系的实体框架 (EF) 代码优先级联删除

实体框架代码优先的性能

EF 6.X 中的实体框架代码优先 Fluent API 默认值

实体框架代码优先开发资源和文档

在快速自定义的NopCommerce中使用实体框架(EF)代码优先迁移

实体框架 4 代码优先的优缺点 [关闭]