Смешивание Table-Per-Hierarchy и Table-Per-Type в Entity Framework Code First для существующей базы данных

tl;dr: я пытаюсь сопоставить модель code-first с существующей базой данных, в которой определенная иерархия объектов имеет смешанную схему наследования. Некоторые конкретные классы используют TPH, а некоторые используют TPT. Кажется, я не могу получить правильное отображение.


У меня есть иерархия объектов, которые я пытаюсь сопоставить с существующей базой данных. Некоторые из конкретных классов содержат дополнительные свойства, поэтому у них есть собственная таблица; некоторые из конкретных классов этого не делают, поэтому они живут в базовой таблице и полагаются на столбец дискриминатора. Для упрощения я создал POC. Структура базы данных такая:

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
)

И эквивалентными POCO будут:

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

public class FooTph : Foo {}

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

Кажется достаточно простым. Итак, моей первой попыткой было следующее сопоставление (свободное или с атрибутами, результат тот же):

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));

Но это не сработало, потому что:

  1. Он хочет создать дескриминатор FooTpt.FooType в таблице FooTpts
  2. #P6# <блочная цитата> #P7#

Вернуться к доске для рисования. В этом ответе предлагается создать промежуточный абстрактный объект, сопоставленный с родительской (TPH) таблицей. Все всегда можно решить с помощью еще одного уровня абстракции, верно? Итак, я делаю несколько изменений:

+ public abstract class FooTptBase : Foo {}

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

И измените сопоставление:

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");

Теперь база данных выглядит хорошо, и у нас есть единственный дискриминатор в родительской таблице. Но чего-то все равно не хватает и мы получаем ту же ошибку:

(6,10): ошибка 3032: проблема с сопоставлением фрагментов, начинающихся со строк 6, 11: EntityTypes ConsoleApplication1.FooTph, ConsoleApplication1.FooTpt сопоставляются с одними и теми же строками в таблице Foo. Условия сопоставления можно использовать для различения строк, которым сопоставлены эти типы.

Это на самом деле не имеет смысла, потому что все FooTpt должны быть FooTptBase по определению, для чего требуется FooType == 2. (Как будто построитель модели игнорирует мой промежуточный FooTptBase абстрактный тип?)

Итак, что мне не хватает? Как я могу выполнить то, что я пытаюсь сделать?


comment
Вы хотите использовать TPT в одних классах и TPH в других или только один подход, если это возможно? Вы знаете, что можете выбирать, какой подход использовать, независимо от структуры класса, верно?   -  person Fabio Luz    schedule 11.09.2015
comment
У меня единая иерархия классов, и я хочу использовать TPT для одних классов и TPH для других. У меня нет проблем с тем, чтобы все одно или все остальные работали, но я не могу заставить его объединить два   -  person lc.    schedule 12.09.2015
comment
Вы когда-нибудь приходили правильно?   -  person Jacques Bosch    schedule 07.03.2016