Опубликовано
- 3 мин чтения
Cтратегия отображения типов и производительность EF

Entity Framework (EF) — это популярный ORM-фреймворк для работы с базами данных в .NET-приложениях. EF поддерживает разные стратегии отображения сущностей, каждая из которых влияет на схему базы данных и взаимодействие EF с иерархией типов .NET. В этой статье мы рассмотрим, как эти стратегии влияют на производительность в SQL Server.
Стратегии отображения в Entity Framework
В официальной документации Microsoft описаны три стратегии:
- Table-per-hierarchy (TPH);
- Table-per-class (TPC);
- Table-per-type (TPT).
Из моего опыта существует ещё одна стратегия, которую я называю TPH с JSON
. Суть её в том, что свойства дочерних классов сериализуются в JSON и сохраняются в одну колонку.
Рассмотрим следующую иерархию:
public class Root { }
public class ChildA : Root { }
public class ChildB : Root { }
public class ChildC : Root { }
Схемы баз данных для этой иерархии будут следующими:
TPH | TPC | TPT | TPH с JSON |
---|---|---|---|
![]() | ![]() | ![]() | ![]() |
Описание:
- В
TPH
все свойства всех сущностей находятся в одной таблице. - В
TPC
дочерние таблицы не связаны с родительской таблицей. - В
TPT
дочерние таблицы содержат только специфичные для типа столбцы и связаны с родительской. - В
TPH с JSON
все специфичные свойства упакованы в одну колонкуPayload
, а тип хранится в колонкеPayloadType
.
Теперь посмотрим, как это влияет на производительность.
Бенчмарк
В проекте бенчмарка есть 4 экземпляра DbContext
, по одному для каждой стратегии. TPH
, TPC
, TPT
стандартные. Для TPH с JSON
требуется дополнительный обёрточный класс:
public class RootWrapper : Root
{
public string Payload { get; set; } = string.Empty;
[NotMapped]
public Root OriginalEntity { get; set; }
public PayloadTypes PayloadType { get; set; }
public enum PayloadTypes
{
Root,
ChildA,
ChildB,
ChildC,
}
}
Для генерации тестовых данных использовалась библиотека Bogus
, для замеров — BenchmarkDotNet
.
Проводились два теста: на INSERT
и SELECT
данных.
Результаты
Вот результаты. Как видно:
TPT
показывает худшие результаты дляSELECT
иINSERT
.TPC
чуть быстрее при выборке, но медленнее при вставке по сравнению сTPH
.TPH с JSON
показал лучшую производительность.

Рисунок 1 — Время выполнения вставки данных

Рисунок 2 — Время выполнения выборки данных
TPH против TPH с JSON
Чтобы разобраться, почему TPH с JSON
быстрее TPH
, я провёл дополнительный бенчмарк: разные сущности содержали от 3 до 21 свойства.

Рисунок 3 — Влияние количества свойств и сущностей на время вставки

Рисунок 4 — Отношение времени вставки TPH к TPH с JSON по количеству сущностей

Рисунок 5 — Отношение времени вставки TPH к TPH с JSON по количеству свойств
Причины ухудшения производительности TPH
:
- EF Core создаёт отдельный
INSERT
для каждого типа. - EF Core создаёт параметр для каждого свойства.
Для TPH с JSON
создаётся один INSERT
, а количество параметров в 4 раза меньше.
Выборка данных
Для выборки (SELECT
) ситуация чуть иная:

Рисунок 6 — Влияние количества свойств и сущностей на время выборки

Рисунок 7 — Отношение времени выборки TPH к TPH с JSON по количеству сущностей

Рисунок 8 — Отношение времени выборки TPH к TPH с JSON по количеству свойств
Если свойств меньше 8, TPH с JSON
быстрее, если больше — выигрывает обычный TPH
.
Вывод
Результаты можно свести в таблицу:
Место | Стратегия | Плюсы | Минусы |
---|---|---|---|
1 | TPH с JSON | Лучшая производительность почти во всех случаях | Требует дополнительного кода, индексирования JSON |
1 | TPH | Стандартная стратегия EF Core, хорошая производительность | Ухудшение производительности при большом количестве типов |
2 | TPC | Чуть лучше при вставке данных | Чуть хуже при выборке данных |
3 | TPT | ??? | Худшая производительность |