Entity Framework + 多级继承 + EF Code first

3
我正在尝试使用Code First设置TPC继承。我有一个三层继承结构。抽象类A,具体类B继承自A,类C继承自B。 类A属性:ID,CreatedBy和CreatedOn。 类B属性:FirstName,LastName,BirthDate 类C属性:Appointmentdate,Status
我希望在数据库中创建类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行对应的10行到CClass中,实体框架会在BClass中插入20行,其中10行具有实际值,而另外10行为空值。CClass则给出了预期的结果,有10行。

第二个问题是当我从CClass查询数据时,我没有得到结果。错误信息如下:

实体集'CClass'未在实体容器'TestContext'中定义。靠近简单标识符,第1行,第12列。

我正在使用Huy Nguyen的帖子中的代码,用于Entity Framework 4 POCO、Repository和Specification Pattern [升级到EF 4.1],网址为http://huyrua.wordpress.com/2011/04/13/entity-framework-4-poco-repository-and-specification-pattern-upgraded-to-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个回答

7

您的映射规范指定使用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表之间是唯一的。可以通过为B指定种子0和增量2以及为C指定种子1和增量2来轻松完成此操作,但每次添加新的子实体类型时都必须进行修改。您的存储库很糟糕。当您使用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<EntityBase>,你必须以与 Employee 相同的方式映射 Person
modelBuilder.Entity<Person>().Map(m => m.MapInheritedProperties()).ToTable("Person"); 

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


谢谢您的回复,Ladislav。但是现在我遇到了不同的错误。 我首先做的事情是按照您上面的代码设置类B和C的映射。错误是“类型'C'不能被定义为映射,因为它从使用实体拆分或其他形式的继承的类型中继承属性。要么选择不映射继承属性的不同继承映射策略,要么更改层次结构中的所有类型以映射继承属性并且不使用拆分。”我知道我需要阅读很多关于EF、Code First等方面的内容,但我需要尽快解决这个问题并使其运行。 - Amit
你能给我提供一个链接或示例,涵盖了确切的情况吗?一旦上述问题得到解决,我将着手重构我的代码库。谢谢。 - Amit
你应该发布你的实体代码,因为可能有一些细节破坏了映射,或者我指错了方向。 - Ladislav Mrnka
请查看上面的实际实体。 - Amit
我理解在Person类上使用MapInheritedProperties()方法可以将EntityBase类中的属性映射到Person表中。然而,在Employee类上使用MapInheritedProperties()方法会导致Employee表中出现与Person表重复的列。如果我不在Employee类上指定MapInheritedProperties()方法,那么它就会再次成为TPC和TPT的组合。对吗? - Amit

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接