EF Core: сущности и DbContext
EF Core: работа с базой данных на C# без ручного SQL.
Суть: Entity Framework Core (EF Core) — это ORM (Object-Relational Mapper): он связывает C#-классы с таблицами БД. Вы пишете запросы на C# (LINQ), а EF Core генерирует SQL, выполняет его и превращает строки обратно в объекты.
Без ORM работа с БД — это руками писать SQL-строки, открывать соединения, читать ридеры и маппить колонки в поля. Долго и хрупко. EF Core берёт это на себя: вы описываете модель классами, а он общается с базой за вас.
Сущности и DbContext
public class User
{
public int Id { get; set; }
public string Name { get; set; } = "";
public string Email { get; set; } = "";
}
public class AppDbContext : DbContext
{
public AppDbContext(DbContextOptions<AppDbContext> options)
: base(options) { }
public DbSet<User> Users => Set<User>();
}
Класс User — это сущность, она станет таблицей. AppDbContext — точка входа к БД; свойство DbSet<User> представляет таблицу пользователей, через которое идут запросы.
Регистрация
builder.Services.AddDbContext<AppDbContext>(options =>
options.UseNpgsql(builder.Configuration.GetConnectionString("Default")));
Строка подключения берётся из appsettings.json. UseNpgsql — для PostgreSQL; есть провайдеры для SQL Server (UseSqlServer), SQLite и других.
Как работает под капотом
EF Core строит модель из ваших классов: по соглашениям Id становится первичным ключом, типы свойств — типами колонок. DbContext внутри хранит change tracker — он отслеживает загруженные объекты и фиксирует изменения. Когда вы вызываете SaveChanges(), EF сравнивает текущее состояние объектов с исходным и генерирует нужные INSERT/UPDATE/DELETE. Путь запроса к данным выглядит так:
[Контроллер]
|
v LINQ-запрос
[DbContext / DbSet]
|
v EF Core строит SQL
[Провайдер (Npgsql)]
|
v SQL по сети
[База данных]
|
v строки таблицы
[EF маппит строки -> объекты User]
|
v
[Контроллер отдаёт JSON]
Частые ошибки
- Регистрировать DbContext как Singleton. Он не потокобезопасен и должен жить один запрос (Scoped) — об этом отдельный урок.
- Класть строку подключения в код. Она содержит логин/пароль — место ей в конфиге/секретах.
- Думать, что EF — это «магия без SQL». SQL всё равно генерируется; полезно уметь его смотреть и понимать.
Best practices
- Держите сущности чистыми (POCO) — без логики работы с БД внутри.
- Выбирайте провайдер под вашу БД; для учебных проектов удобен SQLite (файл вместо сервера).
- Включайте логирование SQL в dev, чтобы видеть, какие запросы реально уходят в базу.
Соглашения, которыми EF строит модель
EF Core придерживается принципа «convention over configuration»: многое выводится по умолчанию из ваших классов. Свойство Id или <Тип>Id становится первичным ключом. Имя DbSet или класса даёт имя таблицы. Тип C#-свойства превращается в тип колонки (string в text/nvarchar, int в integer). Навигационные свойства и внешние ключи распознаются по именам. Там, где соглашений мало, подключают Fluent API в методе OnModelCreating или атрибуты — для индексов, ограничений длины, точных типов и связей.
Под капотом DbContext — это и единица работы (Unit of Work), и набор репозиториев (DbSet'ы). Он собирает изменения в памяти и применяет их одним SaveChanges в транзакции. Поэтому правильно держать DbContext коротким — на один запрос или одну логическую операцию, а не делить между запросами.
Выбор провайдера и связь с БД
EF Core не привязан к одной СУБД: провайдеры подключаются пакетами — UseNpgsql для PostgreSQL, UseSqlServer для SQL Server, UseSqlite для SQLite, есть и другие. Большая часть вашего кода (сущности, LINQ-запросы, миграции) не зависит от выбора, что упрощает миграцию между базами и тестирование. Для учебных проектов SQLite особенно удобен: вся база — один файл, не нужно поднимать сервер.
Строка подключения — чувствительные данные (хост, логин, пароль), поэтому ей место не в коде, а в конфигурации и секретах. В dev её держат в User Secrets, в проде — в переменных окружения. Включение логирования SQL в Development бесценно: вы видите ровно тот SQL, который EF генерирует из ваших LINQ-запросов, и быстро замечаете неэффективные запросы и лишние обращения к БД ещё до того, как они станут проблемой в проде.
Итог: EF Core связывает классы и таблицы, а DbContext — это ваш шлюз к БД с трекингом изменений. Дальше — как создать схему БД через миграции.