实体框架+多级继承+ EF代码 [英] Entity Framework + Multilevel Inheritance + EF Code first

查看:161
本文介绍了实体框架+多级继承+ EF代码的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用Code First设置TPC继承。我有一个三级的黑社会。
抽象类A,具体类B从A继承,C类继承自B.
A类属性:ID,CreatedBy和CreatedOn。
B类属性:FirstName,LastName,BirthDate
C类属性:Appointmentdate,状态



我想要B类和C类的表在数据库中创建的身份由数据库生成。



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

  public DbSet< ; B个BList {get;组; } 
public DbSet< C> CList {get;组;

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

列表< B> bList = testData.GetBList();

foreach(b objB in bList)
{
context.BList.Add(objB);
}

列表< C> cList = testData.GetCList();
foreach(c objC in cList)
{
context.CList.Add(objC);
}

context.SaveChanges();
}

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


EntitySet'CClass'没有在EntityContainer
'TestContext'中定义。


我正在使用Huy Nguyen的实体框架4的代码POCO,存储库和规范模式[升级到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>()其中TEntity:class 
{
DbContext.Set< TEntity>();
var entityName = GetEntityName< TEntity>();
return((IObjectContextAdapter)DbContext).ObjectContext.CreateQuery< TEntity>(entityName).ToList< TEntity>();
}

私有字符串GetEntityName< TEntity>()其中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;组; }
public virtual string CreatedBy {get;组; }
public virtual DateTime? CreatedOn {get;组; }
public virtual string LastModifiedBy {get;组; }
public virtual DateTime? LastModifiedOn {get;组; }

}


public class Person:EntityBase
{
public virtual string FirstName {get;组; }
public virtual string MiddleName {get;组; }
public virtual string LastName {get;组; }
public virtual DateTime? BirthDate {get;组;


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

public partial class Employee:Person
{
[必需]
public string EmployeeNumber {get ;组; }
[必需]
public string department {get;组; }

public DateTime?加入日期{get;组; }
public DateTime? PromotionDate {get;组; }
}

我的上下文类现在有:

  public DbSet< EntityBase>人{get;组; 

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

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


解决方案

您的映射指定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,则在B和C表之间必须是唯一的。这可以通过为种子0指定身份和B和种子1的增量2以及C的增量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>人{get;组;如果你想定义 DbSet< EntityBase> 您必须以与 Employee 相同的方式映射 Person 



($)$($)$($)$ {pre> modelBuilder.Entity< Person>()Map(m => m.MapInheritedProperties())ToTable(Person);

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


I'm trying to set up a TPC inheritance using Code First. I have a three level heirarchy. Abstract class A, concrete class B inherits from A and a class C inherits from B. Class A properties: ID, CreatedBy and CreatedOn. Class B properties: FirstName, LastName, BirthDate Class C properties: Appointmentdate, Status

I want tables for class B and class C to be created in the database with identity generated by the database.

I have following code in my context class:

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

I have a contextinitializer class which override Seed method to populate some test data in database.

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

This creates the tables BClass and CClass in the database but the problem is if Iam inserting 10 rows in BClass and corresponding 10 rows in CClass, the entity framework inserts 20 rows in BClass with 10 rows with actual values and 10 rows with Nulls. CClass gives the expected results with 10 rows. The second problem is I don't get the results when i query for the data from CClass. I error message says:

The EntitySet 'CClass' is not defined in the EntityContainer 'TestContext'. Near simple identifier, line 1, column 12.

I am using code from Huy Nguyen's post for Entity Framework 4 POCO, Repository and Specification Pattern [Upgraded to EF 4.1] from http://huyrua.wordpress.com/2011/04/13/entity-framework-4-poco-repository-and-specification-pattern-upgraded-to-ef-4-1/ and my code in generic repository looks like

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

I tried changing my table mappings but it didn't help. I don't know what am i missing here and not sure what to do. I am in urgent need of help.

Thanks in advance.


This is how my actual classes looks like:

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

and my context class now has:

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

解决方案

Your mapping specifies TPT not TPC. You must use:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    base.OnModelCreating(modelBuilder);

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

I want tables for class B and class C to be created in the database with identity generated by the database.

EF will not do this for you. This will require custom modification of generated code because once using inheritance entities must have unique Id among all instances in whole inheritance hierarchy = id must be unique between both B and C tables. This can be easily done by specifying identity with seed 0 and increment 2 for B and seed 1 and increment 2 for C but you must modify this each time you add a new child entity type.

Your repository is terrible. Why are you converting back to ObjectContext when you are using DbContext? Read something about using and querying DbContext and start without repositories. Also ObjectContext API doesn't allow using derived types in query. You must query for top level mapped entity type and use OfType extension method to get only derived types.

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

Just another example where generic approach doesn't work.

Edit:

I'm sorry, I see where the problem is. I was to fast in my description and I skipped one important detail of my solution. I don't have A (EntityBase) mapped because it is not needed. It is marked as abstract entity so it is not needed to have separate table for that which will be always empty in TPC inheritance. That is also a reason why my B (Person) doesn't call MapInheritedProperties in its mapping because I take it as a root of mapped inheritance. Because of that my DbSet defined for my example must be defined as:

public DbSet<Person> People { get; set; }

If you want to define DbSet<EntityBase> you must map Person in the same way as Employee:

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

Your current mapping defines EntityBase and Person as part of TPT inheritance with Employee as TPC inheritance but EF doesn't like combining inheritance types.

这篇关于实体框架+多级继承+ EF代码的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆