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
            });
    }
}
Ciel
  • 17,312
  • 21
  • 104
  • 199

1 Answers1

1

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
  • 360,892
  • 59
  • 660
  • 670
  • 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. – Ciel Oct 14 '10 at 20:19
  • 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 Oct 14 '10 at 20:30
  • It's true that this works for simple type mappings like the Id field, but it doesn't work for mappings like the IList 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. – Ciel Oct 14 '10 at 20:35
  • 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 Oct 14 '10 at 20:37
  • 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 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 method, which doesn't account for Collection associations. – Ciel Oct 14 '10 at 20:39
  • I have updated the code to show what I have tried in relationship to TPC. – Ciel Oct 14 '10 at 20:44
  • 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. – Ciel Oct 14 '10 at 20:52
  • 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 Oct 14 '10 at 21:02
  • Yeah, this is a pretty imperative feature for designing intelligent domain models. They should have a higher level Case 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. – Ciel Oct 14 '10 at 21:05
  • 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 Oct 14 '10 at 21:06
  • 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. – Ciel Oct 14 '10 at 21:09
  • Perhaps we are just missing some important configuration. You will see if anybody else will help you. Sorry that I didn't. – Ladislav Mrnka Oct 14 '10 at 21:22