Что такое AddSingleton в ASP.NET Core DI?

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

Ответ

AddSingleton — это метод расширения для регистрации сервиса с одиночным (Singleton) временем жизни в контейнере внедрения зависимостей (DI) ASP.NET Core.

Поведение: Создается ровно один экземпляр сервиса на все время работы приложения. Этот экземпляр используется для всех запросов и всех пользователей.

Регистрация сервиса:

public void ConfigureServices(IServiceCollection services)
{
    // Регистрация реализации как синглтона
    services.AddSingleton<IMyService, MyService>();

    // Регистрация существующего экземпляра как синглтона
    var instance = new MyService();
    services.AddSingleton<IMyService>(instance);

    // Регистрация типа, который будет использоваться и как сервис, и как реализация
    services.AddSingleton<MyService>();
}

Когда использовать AddSingleton:

  • Сервисы без состояния (Stateless): Кэши (IMemoryCache), клиенты для внешних API (если потокобезопасны), логгеры (фабрика логгеров сама управляет жизненным циклом).
  • Дорогие в создании объекты: Объекты, инициализация которых требует больших ресурсов (например, загрузка конфигурации, установка соединения).
  • Хранилища данных в памяти, которые должны быть доступны всем пользователям (например, in-memory репозиторий для демо).

Критически важные предостережения:

  1. Потокобезопасность (Thread Safety): Поскольку один экземпляр используется одновременно многими запросами, ваша реализация синглтона должна быть потокобезопасной. Используйте конструкции lock, SemaphoreSlim или concurrent-коллекции (ConcurrentDictionary).
  2. Зависимости: Синглтон не должен зависеть от сервисов с временем жизни Scoped (например, DbContext), так как это может привести к утечке памяти или неправильному использованию ресурсов (Scoped-сервис превратится в синглтон).
  3. Disposable: Если синглтон реализует IDisposable, контейнер НЕ вызовет Dispose() автоматически. Утилизацию нужно управлять вручную, например, через IApplicationLifetime.ApplicationStopping.