Какой тип времени жизни (lifetime) вы используете при регистрации сервиса для работы с БД в ASP.NET Core?

«Какой тип времени жизни (lifetime) вы используете при регистрации сервиса для работы с БД в ASP.NET Core?» — вопрос из категории C# Core, который задают на 25% собеседований C# Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Для регистрации контекста базы данных (например, DbContext в Entity Framework Core) в ASP.NET Core наиболее правильным является тип времени жизни Scoped.

Почему Scoped?

  • Безопасность: Один экземпляр DbContext создается и используется в рамках одного HTTP-запроса (или другой области видимости). Это гарантирует, что операции в рамках одного запроса будут изолированы.
  • Производительность: Scoped предотвращает необходимость создавать новый контекст для каждой операции внутри запроса, но и не хранит его глобально, что могло бы привести к утечкам памяти или состоянию гонки.
  • Согласованность: Все репозитории и сервисы в рамках одного запроса работают с одним экземпляром контекста, что обеспечивает корректное отслеживание изменений (change tracking) и работу с транзакциями.

Пример регистрации:

// В Program.cs или Startup.cs
builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")),
    ServiceLifetime.Scoped // Явное указание (по умолчанию и так Scoped)
);

Важные исключения и нюансы:

  • Transient для DbContextплохая практика. Каждый запрос на создание сервиса получит новый контекст, что сломает отслеживание сущностей и может привести к множественным открытым соединениям.
  • Singleton для DbContextкатегорически нельзя. Контекст не потокобезопасен, и его использование из разных потоков одновременно приведет к непредсказуемым ошибкам и повреждению данных.
  • Для не-веб-приложений (консольные, фоновые службы) область видимости (Scoped) создается вручную с помощью IServiceScopeFactory.