0

I have the following supertype/subtype pattern at the database level:

MotherTable
- MainDiscriminator
- CommonAttribute1
- CommonAttribute2

ChildTable1
    - MainDiscriminator (value: 1)
    - AdditionalAttributes...

ChildTable2
    - MainDiscriminator (value: 2)
    - AdditionalAttributes...

Until this point, pretty typical implementation configuration for mapping:

Map<ChildTable1>(m => m.Requires("MainDiscriminator").HasValue("1"));
Map<ChildTable2>(m => m.Requires("MainDiscriminator").HasValue("2"));

Now where I am having issues is configuring second level of inheritances; ChildTable2 has grand children with a different discriminator that MotherTable knows nothing about. Would look like this

ChildTable2
    - MainDiscriminator (value: 2)
    - AdditionalAttributes...
    - SecondaryDiscriminator

GrandChild1
    - SecondaryDiscriminator (value: 1)
    - AdditionalAttributes...

GrandChild2
    - SecondaryDiscriminator (value: 2)
    - AdditionalAttributes...

I tried various approaches, but I either run into misconfiguration stating that two entities are being mapped to same row or EF tries to enter a null value to a non existing column named "Discriminator" in MotherTable.

One of the avenue that seemed to allow to workaround this, was a DbInterceptor removing those unwanted "Discriminator" column inserts/updates/deletes, but I would rather not go there.

Any ideas/thoughts would be appreciated.

6
  • Strange. It seems like you are messing up between "Table per Hierarchy (TpH)" and "Table per Type (TpT)" approach. AFAIK, TpH approach will end up in one single table by using a discriminator, when TpT will connect all tables in one-to-one schema, without a discriminator. But please correct me if I am wrong. Commented Mar 9, 2018 at 10:01
  • @ErlanggaHastoHandoko I actually tried strictly TpT with 3 levels but that did not work out (even when bringing MainDiscriminator all the way down). In most recent it is a mix of both where the grandchilds are in ChildTable2. I thought that would be easier to manage, but seems like it is not. Commented Mar 9, 2018 at 11:17
  • Have you succeeded? Sorry but I still don't understand the problem, because if you stick with TpT, without the discriminator, you will still get exactly what you want. I've done this with Code-First before, with 3 levels of inheritance. Do you start from an existing database? Commented Mar 12, 2018 at 1:01
  • @ErlanggaHastoHandoko more or less with a combination of TpT + TpH but it creates some very long queries... although your statement made me rethink... I am guessing i was trying to force discriminator with .Requires, but with TpT the type is really what matters to insert in right table. How would I force a certain discriminator value in those cases? Commented Mar 12, 2018 at 11:28
  • How about treating them as ordinary columns in EF? Would it make any sense to you? Commented Mar 12, 2018 at 12:44

1 Answer 1

1

By the time I wrote this, you might have already found the solution. Still, I did this so you may get some insight about what other options might have in the table.

I may say that your case is the right one to implement TpT (Table per Type) inheritance in EF 6.

So, this is my take for the case, starting from simple POCO classes :

public class MotherTable
{
    public int MotherId { get; set; }
    public int MainDiscriminator { get; set; }
    public string CommonAttribute1 { get; set; }
    public string CommonAttribute2 { get; set; }
}

public class ChildTable1 : MotherTable
{
    public string AdditionalAttribute1 { get; set; }
}

public class ChildTable2 : MotherTable
{
    public int SecondaryDiscriminator { get; set; }
    public string AdditionalAttribute2 { get; set; }
}

public class GrandChild1 : ChildTable2
{
    public string AdditionalAttributes21 { get; set; }
}

public class GrandChild2 : ChildTable2
{
    public string AdditionalAttributes22 { get; set; }
}

Setup in EF 6 using fluent API (code-first scenario) like this :

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity<MotherTable>().ToTable("MotherTable").HasKey<int>(c => c.MotherId);
    modelBuilder.Entity<ChildTable1>().ToTable("ChildTable1");
    modelBuilder.Entity<ChildTable2>().ToTable("ChildTable2");
    modelBuilder.Entity<GrandChild1>().ToTable("GrandChild1");
    modelBuilder.Entity<GrandChild2>().ToTable("GrandChild2");
}

Tested by running this snippet :

using (var context = new StackoverflowEntities())
{
    var account1 = new GrandChild1
    {
        MotherId = 1,
        MainDiscriminator = 2,
        CommonAttribute1 = "Mother common 1",
        CommonAttribute2 = "Mother common 2",
        AdditionalAttribute2 = "Child Add Attr 2",
        AdditionalAttributes21 = "Grand Child Add Attr 2.1",
        SecondaryDiscriminator = 1
    };

    var accounts1 = context.Set<GrandChild1>();
    accounts1.Add(account1);

    var account2 = new GrandChild2
    {
        MotherId = 2,
        MainDiscriminator = 2,
        CommonAttribute1 = "Mother common 1",
        CommonAttribute2 = "Mother common 2",
        AdditionalAttribute2 = "Child Add Attr 2",
        AdditionalAttributes22 = "Grand Child Add Attr 2.2",
        SecondaryDiscriminator = 2
    };

    var accounts2 = context.Set<GrandChild2>();
    accounts2.Add(account2);

    context.SaveChanges();
}

It will generate a database schema like this :

enter image description here

and will distribute the inserted data (from the snippet above), like this :

enter image description here

I hope this post can give you something to consider, even if you've fixed your codes and it's not exactly what you're looking for. Or maybe you could tell me something I don't know, if any.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.