Ответ
Дискриминатор — это специальный столбец, который Entity Framework Core автоматически добавляет в таблицу базы данных при использовании стратегии наследования TPH (Table Per Hierarchy). Значение в этом столбце определяет, к какому конкретному типу-наследнику относится каждая строка в таблице.
Зачем это нужно? TPH позволяет хранить всю иерархию классов в одной таблице, что упрощает запросы (не нужны JOIN), но требует способа различать типы. Дискриминатор решает эту проблему.
Пример настройки:
// Модель
public abstract class BillingDetail
{
public int Id { get; set; }
public string Owner { get; set; }
public string Number { get; set; }
}
public class BankAccount : BillingDetail
{
public string BankName { get; set; }
public string Swift { get; set; }
}
public class CreditCard : BillingDetail
{
public int CardType { get; set; }
public string ExpiryMonth { get; set; }
}
// Конфигурация в DbContext
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<BillingDetail>()
.HasDiscriminator<string>(nameof(BillingDetailType)) // Имя столбца
.HasValue<BankAccount>("BankAccount") // Значение для BankAccount
.HasValue<CreditCard>("CreditCard"); // Значение для CreditCard
}
Результирующая таблица в БД:
CREATE TABLE BillingDetails (
Id INT PRIMARY KEY,
Owner NVARCHAR(MAX),
Number NVARCHAR(MAX),
BankName NVARCHAR(MAX), -- NULL для CreditCard
Swift NVARCHAR(MAX), -- NULL для CreditCard
CardType INT, -- NULL для BankAccount
ExpiryMonth NVARCHAR(MAX), -- NULL для BankAccount
BillingDetailType NVARCHAR(MAX) NOT NULL -- Дискриминатор
);
Важные моменты:
- Имя столбца и значения можно кастомизировать, как показано выше.
- Тип дискриминатора может быть
string,int,enum. - При запросе к
DbSet<BillingDetail>EF Core автоматически добавляет в SQL условиеWHERE Discriminator = '...'в зависимости от того, запрашиваете вы базовый класс или конкретного наследника. - Недостаток TPH: Столбцы, специфичные для наследников, должны допускать
NULL, что может привести к "раздуванию" таблицы при глубоких иерархиях.