开发者

Entity Framework + Multilevel Inheritance + EF Code first

开发者 https://www.devze.com 2023-03-23 02:16 出处:网络
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.

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开发者_Python百科 = 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.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号