Entity Framework Многоуровневое наследование с TPH

Я работаю с устаревшей системой, которая реализует TPH для определенного количества элементов. Итак, текущая структура выглядит так:

 Abstract Class 1     Abstract Class 2     Abstract Class 3
         |                    |                    |
     ---------            ---------            ---------
    |    |    |          |    |    |          |    |    |
    T1   T2   T3        T4    T5   T6         T7   T8   T9

Таким образом, Type (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

Очевидно, что есть некоторые недостатки дизайна с первоначальным дизайном, и никто не ожидал, что должен захватить список всех автомобилей без запроса трех разных таблиц. Поэтому мой вопрос заключается в том, что с существующей структурой есть способ добавить эту новую иерархию в структуру сущности. Я думал что-то вроде этого

  Vehicle
  -------
  VehicleId
  TypeId (Boat, Plane, Car, etc)
  ItemFK (BoatID, PlaneId, CarId)

Возможно ли это? Есть ли способ сопоставить их в структуре сущностей? Кажется, я не могу их правильно подобрать. Кажется, что это могло бы работать, если бы мы заменили BoatId, PlaneId и CarId с помощью VehicleId (например Условное сопоставление в платформе Entity Framework - ИЛИ с помощью TPH), но в этот момент мы бы сделали действительно инвазивное изменение схемы, которое на самом деле не вариант, и я не уверен, что это даже сработает. По сути, мне нужен способ сопоставления существующих ключей с новой иерархией. Любая помощь приветствуется. Я в недоумении и не могу найти решения, которое отвечает на мой вопрос.

Ответ 1

Вы можете использовать эту структуру

введите описание изображения здесь

 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
        ///
    }

Ответ 2

Проблема состоит в том, что все эти элементы фактически разделяют небольшую общность, но нет способа собрать все эти элементы в коллекцию.

Возможно, у вас могут быть типы в каждой иерархии, реализующие общий интерфейс? Поскольку каждая иерархия уже является отдельной таблицей, похоже, что вы не получите многого - и кажется, что это не стоило бы хлопот - добавив общий базовый класс.

Ответ 3

Соглашения TPH/TPC могут быть определены на основе DbSet<>, которые вы определяете в своем DbContext. Например, вместо объявления DbSet<> для производного типа T вы объявляете только DbSet<> для каждого абстрактного типа. Затем вы можете запросить абстрактные классы отдельно с соответствующими DbSet<> или всеми абстрактными классами с DbSet<> базового абстрактного типа.

В базовом абстрактном классе должно быть задано хотя бы одно поле, поэтому Code-First Migrations будет генерировать таблицу для типа. Наиболее логичным полем для определения будет ПК. Однако миграция текущих данных не будет работать из-за столкновений PK между абстрактными классами (как вы указали в комментарии).

Другая возможность заключается в том, что Entity Framework будет корректно запрашивать все абстрактные типы при запросе DbSet<> базового абстрактного типа, даже если в базе данных нет базового абстрактного типа (поскольку базовый абстрактный тип не имеет поля определены). Тем не менее, я не сталкивался с этим сценарием раньше, поэтому не могу с уверенностью сказать, будет ли это работать или нет.