Смешивание таблиц на иерархии и таблиц на основе типов в коде Entity Framework с существующей базой данных
tl; dr: я пытаюсь отобразить модель кода в существующую базу данных, в которой определенная иерархия объектов имеет смешанную схему наследования. Некоторые конкретные классы используют 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));
Но это не сработало, потому что:
- Хочет создать
FooTpt.FooType
описатель вFooTpts
Таблица Попытка выполнить команду дает мне следующую ошибку (предположительно из-за пункта 1 выше):
(6,10): ошибка 3032: проблема в отображении фрагментов, начинающихся со строк 6, 11:EntityTypes ConsoleApplication1.FooTph, ConsoleApplication1.FooTpt отображаются в те же строки в таблице Foo. Условия сопоставления могут использоваться для различения строк, в которые сопоставляются эти типы.
Вернуться к доске для рисования. В этом ответе предлагается создать промежуточную абстрактную сущность, сопоставленную с родительской таблицей (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
абстрактный тип?)
Итак, что мне не хватает? Как я могу выполнить то, что я пытаюсь сделать?