Многоуровневое наследование Entity Framework с TPH
Я работаю с устаревшей системой, которая реализует TPH для определенного количества предметов. Таким образом, текущая структура выглядит следующим образом
Abstract Class 1 Abstract Class 2 Abstract Class 3
| | |
--------- --------- ---------
| | | | | | | | |
T1 T2 T3 T4 T5 T6 T7 T8 T9
Таким образом, Тип (T*) является дискриминатором для всех таблиц, но поскольку некоторые типы имеют общие столбцы, существует значительное количество различных таблиц. Проблема заключается в том, что все эти элементы на самом деле имеют небольшую общность, но невозможно собрать все эти элементы в коллекцию. На самом деле иерархия должна выглядеть примерно так.
--------------- Base Abstract 1 ----------
| | |
Abstract Class 1 Abstract Class 2 Abstract Class 3
| | |
--------- --------- ---------
| | | | | | | | |
T1 T2 T3 T4 T5 T6 T7 T8 T9
По сути, у нас есть TPT, где каждая таблица для каждого типа является TPH. Для примера из реального мира, вот что нам нужно.
--------------- Vehicle ---------------
| | |
Car Boat Plane
| | |
--------- --------- ---------
| | | | | | | | |
BMW Toyota Fiat T4 T5 T6 T7 T8 T9
Очевидно, что есть некоторые недостатки дизайна с первоначальным дизайном, и никто не ожидал, что потребуется собрать список всех транспортных средств, не запрашивая 3 разных таблицы. Таким образом, мой вопрос в том, чтобы с существующей структурой был способ добавить эту новую иерархию в структуру сущности. Я думал что-то вроде этого
Vehicle
-------
VehicleId
TypeId (Boat, Plane, Car, etc)
ItemFK (BoatID, PlaneId, CarId)
Это возможно? Есть ли способ сопоставить их в рамках сущности? Я не могу соответствовать им правильно. Кажется, это могло бы сработать, если бы мы заменили BoatId, PlaneId и CarId на VehicleId (например, Conditional Mapping в Entity Framework - ИЛИ операция с TPH), но в этот момент мы будем делать действительно инвазивное изменение схемы, которое на самом деле не является вариант, и я не уверен, что будет работать. По сути, мне нужен способ отобразить существующие ключи в новую иерархию. Любая помощь с благодарностью. Я в растерянности и не могу найти решение, которое отвечает на мой вопрос.
3 ответа
Вы могли бы использовать эту структуру
public class Vehicle
{
[Key]
public int Id { set; get; }
///
// common properties
///
public Car Car { set; get; }
public Boat Boat { set; get; }
public Plane Plane { set; get; }
}
public class Car
{
[Key, ForeignKey("Vehicle")]
public int VehicleId { set; get; }
public Vehicle Vehicle { set; get; }
///
// Car properties
///
}
public class Boat
{
[Key, ForeignKey("Vehicle")]
public int VehicleId { set; get; }
public Vehicle Vehicle { set; get; }
///
// Boat properties
///
}
public class Plane
{
[Key, ForeignKey("Vehicle")]
public int VehicleId { set; get; }
public Vehicle Vehicle { set; get; }
///
// Plane properties
///
}
Проблема заключается в том, что все эти элементы на самом деле имеют небольшую общность, но невозможно собрать все эти элементы в коллекцию.
Возможно, вы могли бы иметь типы в каждой иерархии для реализации общего интерфейса? Поскольку каждая иерархия уже представляет собой отдельную таблицу, кажется, что вы не многого выиграете - и кажется, что это не стоило бы хлопот - добавив общий базовый класс.
Соглашения TPH/TPC могут быть определены на основе DbSet<>
что вы определяете в своем DbContext
, Например, вместо объявления DbSet<>
по производному типу T
Вы только заявляете DbSet<>
для каждого абстрактного типа. Затем вы можете запросить абстрактные классы индивидуально с соответствующими им DbSet<>
или все абстрактные классы с DbSet<>
базового абстрактного типа.
В базовом абстрактном классе должно быть определено хотя бы одно поле, поэтому в ходе миграции кода будет сгенерирована таблица для типа. Наиболее логичным полем для определения будет PK. Однако миграция текущих данных не будет работать из-за коллизий PK между абстрактными классами (как вы указали в комментарии).
Другая возможность состоит в том, что Entity Framework будет правильно запрашивать все абстрактные типы, когда вы запрашиваете DbSet<>
базового абстрактного типа, даже если в базе данных нет таблицы для базового абстрактного типа (поскольку базовый абстрактный тип не имеет определенных полей). Однако я не сталкивался с этим сценарием раньше, поэтому не могу с уверенностью сказать, сработает он или нет.