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:
- It wants to create the
FooTpt.FooType
descriminator in theFooTpts
table 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 FooTpt
s 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?