Ответ
Entity Framework Core (EF Core) поддерживает три основные стратегии маппинга наследования объектов на реляционные таблицы. Выбор стратегии влияет на производительность и сложность схемы БД.
1. TPH (Table Per Hierarchy) — Одна таблица для всей иерархии
- Как работает: Все классы иерархии маппятся на одну таблицу. Создается дискриминаторная колонка (например,
Discriminator), которая хранит тип объекта. - Плюсы: Простая схема БД, быстрые запросы без JOIN.
- Минусы: Может привести к "раздутой" таблице с множеством NULL-колонок (свойства, специфичные для подклассов). Нарушает нормализацию.
- Настройка (по умолчанию в EF Core):
public abstract class Vehicle
{
public int Id { get; set; }
public string Model { get; set; }
}
public class Car : Vehicle
{
public int TrunkSize { get; set; } // Специфичное свойство
}
public class Motorcycle : Vehicle
{
public bool HasSidecar { get; set; } // Специфичное свойство
}
// В контексте
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// TPH используется по умолчанию. Дискриминатор добавляется автоматически.
modelBuilder.Entity<Vehicle>()
.HasDiscriminator<string>("VehicleType")
.HasValue<Car>("Car")
.HasValue<Motorcycle>("Motorcycle");
}
Результирующая таблица Vehicles: |
Id | Model | VehicleType | TrunkSize | HasSidecar |
|---|---|---|---|---|---|
| 1 | Model S | Car | 500 | NULL | |
| 2 | Ninja | Motorcycle | NULL | true |
2. TPT (Table Per Type) — Таблица для каждого типа
- Как работает: Для каждого класса в иерархии создается отдельная таблица. Таблица для базового класса содержит общие поля. Таблицы для производных классов содержат свои специфичные поля и внешний ключ на таблицу базового класса.
- Плюсы: Нормализованная схема, нет NULL-колонок.
- Минусы: Сложные запросы с JOIN, потенциально ниже производительность.
- Настройка:
// В контексте
modelBuilder.Entity<Vehicle>().ToTable("Vehicles");
modelBuilder.Entity<Car>().ToTable("Cars"); // Специфичные свойства в отдельной таблице
modelBuilder.Entity<Motorcycle>().ToTable("Motorcycles");
3. TPC (Table Per Concrete Type) — Таблица для каждого конкретного типа
- Как работает: Для каждого конкретного (не абстрактного) класса создается отдельная таблица, которая содержит ВСЕ его поля, включая унаследованные от родителя. Общие свойства дублируются в каждой таблице.
- Плюсы: Избегает JOIN для запросов к конкретному типу.
- Минусы: Дублирование данных (общие поля), сложности с генерацией уникальных ключей (Id) across tables.
- Настройка (EF Core 7.0+):
modelBuilder.Entity<Vehicle>().UseTpcMappingStrategy();
modelBuilder.Entity<Car>().ToTable("Cars");
modelBuilder.Entity<Motorcycle>().ToTable("Motorcycles");
Выбор стратегии:
- TPH — по умолчанию, хорош для простых иерархий с небольшим количеством подклассов и свойств.
- TPT — подходит, когда важна нормализация БД и есть много специфичных свойств.
- TPC — эффективен, когда запросы обычно выполняются в контексте конкретного типа, а не всей иерархии.