Ответ
Контроллеры в ASP.NET Core по умолчанию имеют жизненный цикл Scoped (с областью действия), что означает создание нового экземпляра для каждого HTTP-запроса. Это обеспечивает изоляцию и безопасность данных между запросами.
Последовательность этапов для одного запроса:
- Разрешение зависимостей: DI-контейнер создает экземпляр контроллера, разрешая все зависимости, указанные в его конструкторе (например, сервисы, логгеры).
- Выполнение фильтров: Выполняются различные фильтры (авторизации, ресурсов, действий, исключений, результатов) в определенном порядке.
- Вызов метода действия (Action Method): Вызывается соответствующий запросу метод контроллера, который выполняет бизнес-логику и возвращает
IActionResult. - Выполнение результата: Фреймворк выполняет возвращенный результат (рендерит View, возвращает JSON и т.д.).
- Освобождение ресурсов: Если контроллер реализует интерфейс
IDisposableилиIAsyncDisposable, вызывается соответствующий методDisposeдля очистки неуправляемых ресурсов. После этого экземпляр контроллера готов к сборке мусора.
public class OrderController : Controller, IDisposable
{
private readonly IOrderService _orderService;
private readonly ILogger<OrderController> _logger;
private bool _disposed = false;
// 1. DI-контейнер инжектирует зависимости при создании
public OrderController(IOrderService orderService, ILogger<OrderController> logger)
{
_orderService = orderService;
_logger = logger;
}
// 3. Вызов метода действия
public IActionResult Get(int id)
{
var order = _orderService.GetOrder(id);
return Ok(order); // 4. Выполнение результата
}
// 5. Освобождение ресурсов (вызывается фреймворком)
public void Dispose()
{
if (!_disposed)
{
_logger.LogTrace("OrderController disposed.");
// Освобождение неуправляемых ресурсов, если они есть
_disposed = true;
}
}
}
Важно: Не храните состояние (данные) в полях контроллера, которое должно сохраняться между запросами, так как для нового запроса будет создан новый экземпляр.
Ответ 18+ 🔞
Ну ты смотри, как эти контроллеры в ASP.NET Core живут, а? Ситуация, в общем-то, простая, но народ иногда путается, думает, что они как синглтоны какие-то, один на всех. Хуй там, блядь!
По умолчанию жизненный цикл у них — Scoped, то есть с областью видимости. Проще говоря, на каждый новый HTTP-запрос тебе выкатывают свеженький, только что с завода, экземпляр контроллера. Это, в принципе, логично и безопасно: один запрос — своя песочница, свои данные, ни с кем не пересекается. Красота.
А вот как это всё по кочкам бежит, когда запрос прилетает:
- Втыкаем зависимости. DI-контейнер, этот хитрожопый механизм, смотрит на конструктор контроллера, видит там
IOrderService,ILoggerи прочую хуйню, и говорит: «Ага, щас мы тебе всё это соберём!». И создаёт инстанс, начиняя его всем необходимым. - Пробегаем через фильтры. Тут начинается цирк с конями: сначала фильтры авторизации («а можно ли тебе вообще сюда?»), потом ресурсов, действий, и если что-то пошло не так — фильтры исключений. Порядок важен, ёпта!
- Работа метода. Наконец-то вызывается тот самый метод действия (Action), который ты и писал. Там твоя бизнес-логика, обращения к сервисам — в общем, вся соль. В конце он должен вернуть какой-нибудь
IActionResult. - Исполнение результата. Фреймворк берёт этот результат и делает что надо: рендерит вьюху, отдаёт JSON, редиректит — в общем, выполняет свою работу.
- Зачистка. Всё, запрос отработан. Если твой контроллер умный и реализует
IDisposable, то фреймворк вызовет методDispose(), чтобы ты мог прибраться, если использовал какие-нибудь неуправляемые ресурсы. После этого экземпляр отправляется на свалку истории, то есть ждёт сборщика мусора.
Вот, смотри, пример, чтобы вообще всё стало ясно, как божий день:
public class OrderController : Controller, IDisposable
{
private readonly IOrderService _orderService;
private readonly ILogger<OrderController> _logger;
private bool _disposed = false;
// 1. Сюда DI-контейнер всунет все зависимости при создании
public OrderController(IOrderService orderService, ILogger<OrderController> logger)
{
_orderService = orderService;
_logger = logger;
}
// 3. Вот он, наш метод, который и вызывается
public IActionResult Get(int id)
{
var order = _orderService.GetOrder(id);
return Ok(order); // 4. И сразу результат
}
// 5. А это фреймворк вызовет, когда всё кончится, для прибранья
public void Dispose()
{
if (!_disposed)
{
_logger.LogTrace("OrderController disposed.");
// Тут бы почистил что-то, если бы было что чистить
_disposed = true;
}
}
}
И главное, запомни раз и навсегда: не храни в полях контроллера никакое состояние, которое должно пережить запрос! Не выдумывай. Для нового запроса — новый экземпляр, и все твои прошлые поля — нихуя не значат. Если нужно что-то сохранить между запросами — сессия, куки, кеш, база данных, на худой конец. А контроллер — он одноразовый, как презерватив, блядь. Использовал и выбросил.