Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ADO EF Code First Generic Intermediate Class Inheritance mapping

I've got the following requirement that works well in the OO space but I can't seem to get it to map back to the DB using ADO EF code first.

I have numrous products each will have different aspects (attributes but not in the sense of code attributes). For instance ring would have aspects such as mineral type = gold etc whilst a diamond would have an aspec of clarity = VVSI1.

As you can see the products very greatly in thier composition and I want a dynamic way of growing my system.

As such I've created a product class:

public class Product
{
    public int id { get; set; }
    public string Name { get; set; }
    private List<ProductAspect> aspects = new List<ProductAspect>();
    public List<ProductAspect> Aspects { get { return aspects; } set { aspects = value; } }
}

It has a list of ProductAspect which is the base class for all aspects moving forward:

public class ProductAspect 
{
    public int id { get; set; }
    public string AspectName { get; set; }
}

I then inherit from the ProductAspect using a generic which alows me to be specific (strongly typed) about my Aspect Value:

public abstract class ProductAspect<T> : ProductAspect
{
    public T AspectValue { get; set; }
}

I then create some Aspects that will allow me to decorate my product:

public class StringAspect : ProductAspect<string> { };
public class DecimalAspect : ProductAspect<decimal> { };
public class ImageAspect : ProductAspect<byte[]> { };

I then give the DbContext a try and have tried both TPH and TPC inheritance mappings.

Neither seem to work. The DB model that get's generated doesn't create a foriegn key to the StringAspect or DecimalAspect tables from the Aspect Table.

public class IxamDataContext : DbContext
{
    public DbSet<Product> Products { get; set; }
    public DbSet<ProductAspect> Aspects { get; set; }

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

    private void AspectMapping(DbModelBuilder mb)
    {
        //TPH
        //mb.Entity<ProductAspect>()
        //    .Map<StringAspect>(m => m.Requires("type").HasValue("sa"))
        //    .Map<DecimalAspect>(m => m.Requires("type").HasValue("da"));

        //TPC
        //mb.Entity<StringAspect>().ToTable("StringAspect");
        //mb.Entity<DecimalAspect>().ToTable("DecimalAspect");
    }
}

Resulting in the following exception for this Seeding code:

        Product p = new Product();
        p.Name = "Diamond";
        p.Aspects.Add(new StringAspect() { AspectName = "History", AspectValue = "Old and long" });
        p.Aspects.Add(new DecimalAspect() { AspectName = "Weight", AspectValue= 96.5M });


        context.Products.Add(p);
        context.SaveChanges();

Excpetion:

EntityType 'StringAspect' does not exist in the EntitySet 'IxamDataContext.Aspects'. Parameter name: entity

Any ideas from the EF code first pros out there?

like image 392
Hugo Avatar asked Apr 25 '11 08:04

Hugo


1 Answers

Entity framework doesn't support intermediate non mapped types in inheritance hierarchy. It means that you can't have this inheritance: A (mapped) -> B (not mapped) -> C (mapped). EF also doesn't support mapping generic types. It means that you must remove your generic intermediate class from the hierarchy and move AspectValue to derived types with correct type.

like image 54
Ladislav Mrnka Avatar answered Oct 21 '22 12:10

Ladislav Mrnka