Время жизни служб: Transient, Scoped, Singleton
Узнайте, как время жизни Transient, Scoped и Singleton влияет на создание, совместное использование и освобождение объектов.
«Время жизни служб: Transient, Scoped, Singleton» — бесплатный урок C# Academy на CoddyKit. Это урок 2 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения C# Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс C# Academy содержит 4 уроков всего.
Почему важен срок действия
Срок действия службы определяет как долго существует экземпляр и сколько экземпляров создаётся. Неправильный выбор приводит к трудноуловимым ошибкам, например утечке данных между запросами или чрезмерному созданию объектов.
Служба с временным сроком действия: новый экземпляр каждый раз
Службы с временным сроком действия создаются заново при каждом обращении к ним из контейнера. Используйте их для лёгких служб без состояния, если совместное использование состояния может быть опасным.
builder.Services.AddTransient<IEmailSender, SmtpEmailSender>();
// Each resolution creates a new instance:
// var a = sp.GetRequiredService<IEmailSender>(); // new
// var b = sp.GetRequiredService<IEmailSender>(); // new (different)Служба с областью действия: одна на запрос
Службы с областью действия создаются один раз для каждого scope — обычно для одного HTTP-запроса в ASP.NET Core. В рамках запроса используется один и тот же экземпляр, а для следующего запроса создаётся новый.
builder.Services.AddScoped<AppDbContext>();
// Within a single HTTP request:
// Both resolve to the SAME AppDbContext instance
// -> consistent unit of work across the requestОдиночная служба: одна на всё время работы приложения
Одиночные службы создаются один раз и повторно используются на протяжении всего времени работы приложения. Используйте их для ресурсоёмких при создании и безопасных в многопоточном режиме служб, например кэшей конфигурации.
builder.Services.AddSingleton<IMemoryCache, MemoryCache>();
// Or register a pre-built instance:
var config = new AppConfig { MaxRetries = 3 };
builder.Services.AddSingleton<IAppConfig>(config);Сравнение всех трёх вариантов
Краткое сравнение:
- С временным сроком действия — новый экземпляр при каждом запросе; утилиты без состояния
- С областью действия — один экземпляр на HTTP-запрос; DbContext, единица работы
- Одиночная — один экземпляр на всё время работы приложения; кэши, конфигурации
Проблема захваченной зависимости
Внедрение службы с областью действия или с временным сроком действия в одиночную службу — распространённая ошибка. Одиночная служба удерживает ссылку на краткоживущую службу, из-за чего та живёт слишком долго.
// BAD: Singleton captures a Scoped service
public class MySingleton
{
private readonly AppDbContext _db; // Scoped!
public MySingleton(AppDbContext db) => _db = db;
// _db is now stuck alive for the app's lifetime
}Обнаружение нарушений области действия
Включите ValidateScopes, чтобы контейнер выдавал ошибку при запуске, если обнаружена захваченная зависимость. Это позволяет исправить ошибки до того, как они приведут к проблемам с данными в рабочей среде.
builder.Host.UseDefaultServiceProvider(options =>
{
options.ValidateScopes = true; // detect captive deps
options.ValidateOnBuild = true; // fail fast at start
});Службы с областью действия вне запроса
Чтобы получить службы с областью действия в фоновой задаче, создайте явную область с помощью IServiceScopeFactory. Никогда не получайте такие службы напрямую из корневого контейнера.
public class MyWorker : BackgroundService
{
private readonly IServiceScopeFactory _factory;
public MyWorker(IServiceScopeFactory factory) => _factory = factory;
protected override async Task ExecuteAsync(CancellationToken ct)
{
using var scope = _factory.CreateScope();
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
await db.Users.ToListAsync(ct);
}
}Освобождаемые службы с временным сроком действия
Службы IDisposable с временным сроком действия, полученные из корневого контейнера, отслеживаются до освобождения контейнера (завершения работы приложения). Получайте их внутри области, чтобы своевременно освобождать ресурсы.
// Correctly scoped transient disposable
using var scope = sp.CreateScope();
var svc = scope.ServiceProvider.GetRequiredService<MyDisposableService>();
await svc.DoWorkAsync();
// svc.Dispose() called when scope is disposedВыбор подходящего срока действия
Простое руководство:
- Служба не имеет состояния? → С временным сроком действия
- Она зависит от запроса (например, от удостоверения пользователя или DbContext)? → С областью действия
- Она безопасна в многопоточном режиме и требует больших затрат на создание? → Одиночная
Практический пример: три срока действия вместе
Обычно веб-API регистрирует разные службы с подходящими сроками действия для обеспечения корректности и производительности.
builder.Services.AddSingleton<IConfiguration>(builder.Configuration);
builder.Services.AddSingleton<ICacheService, RedisCacheService>();
builder.Services.AddScoped<AppDbContext>();
builder.Services.AddScoped<IOrderRepository, OrderRepository>();
builder.Services.AddScoped<OrderService>();
builder.Services.AddTransient<IEmailSender, SendGridEmailSender>();Быстрая проверка
Какая проблема возникает, когда одиночная служба удерживает ссылку на службу с областью действия?
Итоги: сроки действия служб
Главное:
- С временным сроком действия: новый экземпляр при каждом обращении — подходит для служб без состояния и с небольшими затратами на создание
- С областью действия: один экземпляр на запрос — DbContext, репозитории, единица работы
- Одиночная: один экземпляр навсегда — кэши, конфигурация, безопасные в многопоточном режиме утилиты
- Никогда не внедряйте краткоживущие службы в службы с более длительным сроком действия (захваченная зависимость)
- Используйте
IServiceScopeFactory, чтобы получать службы с областью действия в фоновых задачах
Часто задаваемые вопросы
Урок «Время жизни служб: Transient, Scoped, Singleton» бесплатный?
Да — полный текст урока «Время жизни служб: Transient, Scoped, Singleton» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс C# Academy, подпишись на CoddyKit PRO. Курс C# Academy содержит 4 уроков всего.
Чему я научусь в уроке «Время жизни служб: Transient, Scoped, Singleton»?
Узнайте, как время жизни Transient, Scoped и Singleton влияет на создание, совместное использование и освобождение объектов. Ты практикуешь C# Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать C# Academy?
Предыдущий опыт не требуется. C# Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 2 из 4.
Сколько времени занимает урок «Время жизни служб: Transient, Scoped, Singleton»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке C# Academy?
Да. Каждый урок C# Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Основы контейнера внедрения зависимостей
- Время жизни служб: Transient, Scoped, Singleton
- Внедрение через конструктор и интерфейсы
- Шаблоны фабрики и параметров