views:

70

answers:

1

I am having a lot of trouble with 'base types' in the Code Only model of the Entity Framework. I am having a lot of trouble with 'base types' in the Code Only model of the Entity Framework.

When I try to run this code using a DbContext with a DbSet<Template>, I get the following error.

A The navigation property 'Flags' is mapped to two different join tables 'page.flags' and 'template.flags'. Only one mapping of the navigation property may exist

What this says to me is that I cannot map inherited properties. This is quite breaking to a lot of object oriented code design. Is there a known remedy? I realize I can make Layout non-abstract, and have a backing for it, but it's very obvious this is not the intention of the domain model. The abstract class is a foundational base, not the stored model.

I would like to add, if I put the IList<Flag> in the Template class, this code runs. The Id field still works, even through inheritance. I do not understand why this is happening. Can someone enlighten me?

public abstract class Layout
{
    public virtual int Id
    {
        get;
        set;
    }

    public virtual IList<Flag> Flags
    {
        get;
        set;
    }
}

public class Template : Layout
{
    public virtual string Name
    {
        get;
        set;
    }
}

public class Page: Layout
{
}

public class LayoutConfiguration : EntityConfiguration<Layout>
{
    public LayoutConfiguration()
    {
        HasKey(u => u.Id);
        Property(u => u.Id).IsIdentity();

        MapHierarchy().Case<Page>(c => new
        {
            c.Id
        }).ToTable("Pages");

        MapHierarchy().Case<Template>(c => new
        {
            c.Id,
            c.Name
        }).ToTable("Templates");
    }
}
public class TemplateConfiguration : EntityConfiguration<Template>
{
    public TemplateConfiguration()
    {
        Property(o => o.Name).HasMaxLength(64).IsUnicode();

        HasMany(u => u.Flags).WithOptional()
            .Map("template.flags",
            (template, flag) => new {
                Template = template.Id,
                Flag = flag.Id
            });

        MapSingleType(c => new {
            c.Id,
            c.Name
        }).ToTable("templates");
    }
}

public class PageConfiguration : EntityConfiguration<Page>
{
    public PageConfiguration()
    {
        HasMany(c => c.Flags).WithOptional()
            .Map("page.flags",
            (page, flag) => new
            {
                Page = page.Id,
                Flag = flag.Id
            });
    }
}
+1  A: 

When you use base type for your Template entity, you also have to model this inheritance in mapping. It means that you have to write configuration for Layout which will map Id and Flags and configuration for Template which will map Name. There is several approaches of mapping inheritance in EF. You should probably check Table per Hiearchy.

Edit: Based on your comment you are looking for Table per Class + examples for CTP4.

Edit2: Ok. I tested your scenario with navigation property defined in abstract parent class and it really doesn't work if you are trying to map it to multiple tables.

Ladislav Mrnka
However this is against the design. There will be other types that inherit from Layout, they need to have their Flags mapped to their own respective tables.
Stacey
First - your object oriented design doesn't have to correspond to your database design. Second - check the same article but scroll down to Table per Class.
Ladislav Mrnka
It's true that this works for simple type mappings like the Id field, but it doesn't work for mappings like the IList<Flag> field. I would have to define the HasMany relationship in the Layout configuration, and all inheritors would have to share the relationship table. This is what I aim to avoid. Also, the link given is for CTP3, and most of it is outdated.
Stacey
So check Table per Class it should be exactly what you are looking for because there is no mapping for basic class and each child class has to map it its own way.
Ladislav Mrnka
I have, it doesn't work. If I specify a Configuration for Layout, then I have two classes inherit from it, I cannot specify the mapping for the IList<Flag> mapping in the inheriting classes. It expects everything to be configured in the base class, and I cannot tell it to do different mappings for the HasMany relationship based on the type. This only works for the MapHierarchy().Case<T> method, which doesn't account for Collection associations.
Stacey
I have updated the code to show what I have tried in relationship to TPC.
Stacey
Also updated it to show the error generated, using TPC. The navigation property 'Flags' is mapped to two different join tables 'page.flags' and 'template.flags'. Only one mapping of the navigation property may exist.
Stacey
I have to say I'm surprised that it doesn't work. I will have to think about it to understand why this doesn't work.
Ladislav Mrnka
Yeah, this is a pretty imperative feature for designing intelligent domain models. They should have a higher level Case<T> than on MapHierarchy. I can understand if I have to do all of the mapping in the base class, I'm fine with that, but it doesn't seem possible to do that.
Stacey
I'm affraid this will not be only problem of Code First. I'm expecting this error comes directly from EF core but I have to try it with model first.
Ladislav Mrnka
I'm able to do this just fine in nHibernate, as well. Which is why I was so alarmed when it didn't work in EF.
Stacey
Perhaps we are just missing some important configuration. You will see if anybody else will help you. Sorry that I didn't.
Ladislav Mrnka