Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Что такое жизненный цикл?
Жизненный цикл (lifecycle) — это период от создания объекта до его удаления из памяти. В контексте веб-приложений речь идёт о жизненном цикле сервисов в Dependency Injection контейнере.
Три типа жизненных циклов
Transient — новый экземпляр каждый раз
services.AddTransient<IRepository, Repository>();
var repo1 = sp.GetService<IRepository>();
var repo2 = sp.GetService<IRepository>();
// repo1 != repo2
Scoped — один экземпляр на HTTP-запрос
services.AddScoped<IUserService, UserService>();
using (var scope = sp.CreateScope())
{
var svc1 = scope.ServiceProvider.GetService<IUserService>();
var svc2 = scope.ServiceProvider.GetService<IUserService>();
// svc1 == svc2 (один объект в scope)
}
Singleton — один экземпляр для всего приложения
services.AddSingleton<ICache, Cache>();
var cache1 = sp.GetService<ICache>();
var cache2 = sp.GetService<ICache>();
// cache1 == cache2 (всегда один и тот же)
Жизненный цикл Scoped (самый важный)
Scoped идеален для DbContext и Repository, потому что:
services.AddScoped<DbContext, AppDbContext>();
services.AddScoped<IUserRepository, UserRepository>();
// Каждый HTTP запрос получает:
// 1. Новый DbContext
// 2. Новый Repository
// 3. В конце запроса: Dispose() вызовется автоматически
Пример с IDisposable
public class UserService : IDisposable
{
private readonly DbContext _context;
public UserService(DbContext context)
{
_context = context;
Console.WriteLine("UserService создан");
}
public async Task<User> GetAsync(int id)
{
return await _context.Users.FirstOrDefaultAsync(u => u.Id == id);
}
public void Dispose()
{
Console.WriteLine("UserService удалён");
_context?.Dispose();
}
}
// Использование в контроллере
[ApiController]
public class UserController : ControllerBase
{
private readonly IUserService _userService;
public UserController(IUserService userService)
{
_userService = userService;
}
[HttpGet("{id}")]
public async Task<User> GetUser(int id)
{
return await _userService.GetAsync(id);
}
// В конце запроса: _userService.Dispose() вызовется автоматически
}
Ошибка: Captive Dependency
// НЕПРАВИЛЬНО - Singleton использует Scoped
services.AddSingleton<MyService>();
services.AddScoped<DbContext>();
public class MyService
{
public MyService(DbContext context)
{
// DbContext инжектируется один раз и живёт вечно
// Это ПРОБЛЕМА - он не будет создаваться на каждый запрос
}
}
// ПРАВИЛЬНО
public class MyService
{
private readonly IServiceProvider _sp;
public MyService(IServiceProvider sp)
{
_sp = sp;
}
public void DoWork()
{
using (var scope = _sp.CreateScope())
{
var context = scope.ServiceProvider.GetRequiredService<DbContext>();
// Используем context
} // Dispose вызовется здесь
}
}
Таблица сравнения
| Lifetime | Создание | Удаление | Пример |
|---|---|---|---|
| Transient | Каждый раз | Сразу | Stateless утилиты |
| Scoped | На запрос | На конец запроса | DbContext, Services |
| Singleton | Один раз | На выключение | Config, Cache |
Жизненный цикл ASP.NET Core приложения
- Приложение запускается — Singleton объекты создаются
- Запрос приходит — Scoped объекты создаются
- Контроллер работает — использует Scoped и Transient
- Ответ отправлен — Scoped объекты вызывают Dispose()
- Приложение выключается — Singleton вызывают Dispose()
Практическое значение
Почему это важно?
- Правильные lifetimes = оптимальное использование памяти
- Scoped DbContext = каждый запрос видит актуальные данные
- IDisposable = закрытие соединений, освобождение ресурсов
- Ошибки с lifetimes = утечки памяти, зависания
Золотое правило: DbContext и Repository всегда Scoped, Config и Cache Singleton, Stateless утилиты Transient.