← Назад к вопросам

Какое время жизни объекта при его добавлении в DI?

1.8 Middle🔥 192 комментариев

Комментарии (2)

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Время жизни объекта в Dependency Injection (DI) контейнере

В Dependency Injection контейнере .NET время жизни объекта (или жизненный цикл службы) — это один из ключевых концептов, определяющий, как долго экземпляры объектов будут существовать и как они будут повторно использоваться. В ASP.NET Core существует три основных варианта времени жизни, которые управляют поведением создания, хранения и освобождения зависимостей.

Основные типы времени жизни

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

services.AddTransient<IService, ServiceImplementation>();
  • Объект создается заново каждый раз, когда он запрашивается из контейнера.
  • Каждый потребитель получает новый экземпляр.
  • Идеально для легковесных, безгосударственных служб.
  • Пример: сервис валидации, где каждый вызов должен быть независимым.
// Пример использования
public class TransientExample
{
    private readonly IService _service;
    public TransientExample(IService service)
    {
        // Здесь service — новый экземпляр для каждого TransientExample
        _service = service;
    }
}

2. Scoped (Областный)

services.AddScoped<IService, ServiceImplementation>();
  • Объект создается один раз в пределах области (scope).
  • В веб-приложениях область обычно соответствует одному HTTP-запросу.
  • Все потребители в рамках одной области получают один и тот же экземпляр.
  • Используется для служб, требующих состояния в рамках запроса, таких как DbContext в Entity Framework Core.
// Пример области в middleware ASP.NET Core
public class ScopedMiddleware
{
    private readonly RequestDelegate _next;
    public ScopedMiddleware(RequestDelegate next)
    {
        _next = next;
    }
    
    public async Task InvokeAsync(HttpContext context, IService service)
    {
        // service — один и тот же экземпляр в рамках всего запроса
        await service.DoSomething();
        await _next(context);
    }
}

3. Singleton (Одиночный)

services.AddSingleton<IService, ServiceImplementation>();
  • Объект создается один раз за все время работы приложения.
  • Все запросы и потребители используют один и тот же экземпляр.
  • Требует потокобезопасной реализации, если служба используется параллельно.
  • Подходит для кэширования, конфигурации, логгеров.
// Пример Singleton с потокобезопасностью
public class SingletonService : ISingletonService
{
    private readonly object _lock = new object();
    private int _counter = 0;
    
    public int GetNextValue()
    {
        lock (_lock)
        {
            return ++_counter;
        }
    }
}

Практические аспекты и рекомендации

Взаимодействие разных жизненных циклов

  • Важно соблюдать иерархию зависимостей: Singleton не должен зависеть от Scoped или Transient служб, так как это может привести к непредсказуемому поведению (например, к утечке памяти или некорректному состоянию).
  • Scoped службы, внедренные в Singleton, становятся фактически Singleton, что обычно является ошибкой.

Управление памятью и ресурсами

  • Transient службы создаются и уничтожаются часто, что может увеличить нагрузку на сборщик мусора.
  • Singleton службы живут долго, поэтому важно освобождать неуправляемые ресурсы (реализуя IDisposable).
  • Scoped службы автоматически освобождаются в конце области (например, в конце HTTP-запроса).

Регистрация с фабриками

services.AddSingleton<IService>(sp => new ServiceImplementation());
// Или с доступом к другим службам
services.AddScoped<IService>(sp => 
{
    var otherService = sp.GetRequiredService<IOtherService>();
    return new ServiceImplementation(otherService);
});

Работа с несколькими реализациями

services.AddTransient<IService, FirstImplementation>();
services.AddTransient<IService, SecondImplementation>();
// При запросе IEnumerable<IService> будут возвращены все реализации

Пример реального сценария

Представьте веб-приложение для интернет-магазина:

  • Transient: IProductValidator — проверяет корректность данных продукта, не требует состояния.
  • Scoped: ICartService — управляет корзиной покупок в рамках одной сессии пользователя.
  • Singleton: IConfigurationService — загружает настройки приложения один раз при запуске.

Заключение

Правильный выбор времени жизни объекта в DI — это баланс между производительностью, использованием памяти и корректностью состояния. Transient обеспечивает изоляцию, но может создать нагрузку; Scoped идеален для контекста запроса; Singleton экономит ресурсы, но требует осторожности с состоянием. Понимание этих нюансов критически важно для построения масштабируемых и надежных приложений на платформе .NET.