Какое время жизни DbContext в Entity Framework используется и почему
Что проверяют
Понимаете ли вы, что DbContext это единица работы с отслеживанием изменений, а не соединение с базой. Следом спрашивают, как быть с контекстом в фоновом сервисе или синглтоне.
Как отвечать
В ASP.NET Core AddDbContext регистрирует контекст как Scoped, один экземпляр на HTTP-запрос. Почему так:
- контекст не потокобезопасен: вторая операция до завершения первой падает с
InvalidOperationExceptionпро «second operation». Поэтомуawaitна каждом вызове и никакогоTask.WhenAllпо одному контексту; - Change Tracker растёт: каждая загруженная сущность остаётся в нём. Долгоживущий контекст копит память и отдаёт устаревшие данные;
- единица работы: сервисы одного запроса видят одни и те же сущности, а
SaveChangesпишет всё одной транзакцией. Само ничего не сохраняется, безSaveChangesизменения пропадут.
Другие варианты:
Transient: разные сервисы одного запроса получат разные контексты, и общая транзакция развалится;Singleton: неправильно почти всегда. В Development проверка скоупов не даст внедрить scoped-контекст в синглтон;IDbContextFactory<T>черезAddDbContextFactory: контекст создают на операцию,using var db = factory.CreateDbContext(). ДляBackgroundService, параллельной обработки и Blazor Server, где скоуп живёт всё время соединения;AddDbContextPool: экземпляры переиспользуются, состояние сбрасывается. Экономит аллокации под нагрузкой, но в своих полях контекст ничего хранить не должен.
Без фабрики в фоновом сервисе создают скоуп через IServiceScopeFactory.CreateScope() на каждую итерацию.
Где спотыкаются
Считают DbContext соединением и делают синглтоном ради экономии. Соединения держит пул провайдера, контекст для этого не нужен.
Внедряют контекст в BackgroundService через конструктор. Хостед-сервис это синглтон: в Development это упадёт на проверке скоупов, а в продакшене один контекст проживёт всё время работы приложения.
Запускают несколько запросов через Task.WhenAll на одном контексте и ловят исключение о второй операции.