Какие времена жизни объектов вы знаете в IoC-контейнере ASP.NET Core?

«Какие времена жизни объектов вы знаете в IoC-контейнере ASP.NET Core?» — вопрос из категории ASP.NET Core, который задают на 25% собеседований C# Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

IoC-контейнер ASP.NET Core управляет временем жизни зарегистрированных сервисов через три основных варианта, которые критически важны для корректной работы приложения, особенно в веб-сценариях:

  1. Transient (Временный)

    • Поведение: Контейнер создаёт новый экземпляр каждый раз, когда сервис запрашивается (каждый вызов GetService или внедрение через конструктор).
    • Использование: Для лёгких, не имеющих состояния (stateless) сервисов, где каждый вызов должен быть изолирован. Например, простые валидаторы, преобразователи данных.
      services.AddTransient<IEmailValidator, EmailValidator>();
  2. Scoped (В рамках области)

    • Поведение: В рамках одной области (scope) используется один и тот же экземпляр. В веб-приложении новая область создаётся автоматически для каждого HTTP-запроса. Это самый часто используемый вариант.
    • Использование: Для сервисов, которые должны сохранять своё состояние в рамках одного запроса, например, DbContext в Entity Framework Core, репозитории, сервисы бизнес-логики.
      services.AddScoped<IOrderRepository, OrderRepository>();

      Важно: Попытка разрешить Scoped-сервис из Singleton или вне области видимости приведёт к исключению.

  3. Singleton (Одиночка)

    • Поведение: Контейнер создаёт один экземпляр при первом запросе и затем возвращает его на протяжении всей жизни приложения.
    • Использование: Для сервисов, которые дорого создавать (кэши, клиенты для внешних API с пулом соединений) или должны быть единым источником состояния для всего приложения. Необходимо проектировать такие сервисы как потокобезопасные (thread-safe).
      services.AddSingleton<ICacheService, DistributedCacheService>();

Практическое правило: Используйте Scoped по умолчанию для сервисов, работающих с данными запроса. Используйте Transient для простых, не имеющих состояния утилит. Используйте Singleton осознанно, только для сервисов, которые действительно должны существовать в единственном экземпляре.