2

tl;dr: I'm trying to map a code-first model to an existing database where a certain hierarchy of objects has a mixed inheritance scheme. Some concrete classes use TPH and some use TPT. I can't seem to get the mapping correct.


I have a hierarchy of objects which I'm trying to map to an existing database. Some of the concrete classes contain additional properties, so they have their own table; some of the concrete classes do not, so they live in the base table and rely on a discriminator column. To simplify things, I've created a POC. The database structure is like this:

CREATE TABLE Foos (
    Id INT IDENTITY NOT NULL PRIMARY KEY,
    FooType TINYINT NOT NULL
)

CREATE TABLE FooTpts (
    Id INT NOT NULL PRIMARY KEY 
        FOREIGN KEY REFERENCES Foos(Id),
    Value INT NOT NULL
)

And the equivalent POCOs would be:

public abstract class Foo
{
    public int Id { get; set; }
}

public class FooTph : Foo {}

public class FooTpt : Foo
{
    public int Value { get; set; }
}

Seems simple enough. So my first try was the following mapping (fluent or with attributes, the result is the same):

modelBuilder.Entity<Foo>()
    .ToTable("Foos")
    .Map<FooTph>(m => m.ToTable("Foos").Requires("FooType").HasValue(1))
    .Map<FooTpt>(m => m.ToTable("FooTpts").Requires("FooType").HasValue(2));

But that didn't work because:

  1. It wants to create the FooTpt.FooType descriminator in the FooTpts table
  2. Trying to execute a command gives me the following error (presumably because of point #1 above):

    (6,10) : error 3032: Problem in mapping fragments starting at lines 6, 11:EntityTypes ConsoleApplication1.FooTph, ConsoleApplication1.FooTpt are being mapped to the same rows in table Foo. Mapping conditions can be used to distinguish the rows that these types are mapped to.


Back to the drawing board. This answer suggests creating an intermediate abstract entity mapped to the parent (TPH) table. Everything can always be solved with another layer of abstraction, right? So I make a few changes:

+ public abstract class FooTptBase : Foo {}

- public class FooTpt : Foo
+ public class FooTpt : FooTptBase

And change the mapping:

modelBuilder.Entity<Foo>()
    .ToTable("Foos")
    .Map<FooTph>(m => m.ToTable("Foos").Requires("FooType").HasValue(1))
    .Map<FooTptBase>(m => m.ToTable("Foos").Requires("FooType").HasValue(2));

modelBuilder.Entity<FooTpt>().ToTable("FooTpts");

The database now looks good and we have a single discriminator in the parent table. But something is still missing and we get the same error:

(6,10) : error 3032: Problem in mapping fragments starting at lines 6, 11:EntityTypes ConsoleApplication1.FooTph, ConsoleApplication1.FooTpt are being mapped to the same rows in table Foo. Mapping conditions can be used to distinguish the rows that these types are mapped to.

That doesn't really make sense because all FooTpts have to be a FooTptBase by definition, which should require FooType == 2. (It's almost as if the model builder is ignoring my intermediate FooTptBase abstract type?)

So, what am I missing? How can I accomplish what I'm trying to do?

Community
  • 1
  • 1
lc.
  • 113,939
  • 20
  • 158
  • 187
  • do you want to use TPT in some classes and TPH in others or just one approach if possible? You know that you can choose what approach to use, regardless on class structure, right? – Fabio Sep 11 '15 at 14:29
  • I have a single class hierarchy and I want to use TPT for some classes and TPH for others. I have no problem getting all of one or all of the other to work, but I can't get it to combine the two – lc. Sep 12 '15 at 02:55
  • Did you ever come right? – Jacques Bosch Mar 06 '16 at 22:55

0 Answers0