Когда создается контроллер в ASP.NET Core?

Ответ

По умолчанию новый экземпляр контроллера создается для каждого HTTP-запроса, который соответствует его маршруту. Это обеспечивает изоляцию состояния между запросами и является рекомендуемой практикой.

Почему так? Это поведение (Transient lifetime) гарантирует, что поля контроллера не будут неявно использоваться для хранения данных между запросами, что предотвращает ошибки, связанные с параллелизмом.

Как это изменить? Вы можете зарегистрировать контроллер с другим временем жизни в DI-контейнере, но это требует осторожности.

// В Program.cs или Startup.ConfigureServices
// Scoped — один экземпляр на запрос (по умолчанию для контроллеров через AddControllers)
services.AddScoped<HomeController>();

// Singleton — один экземпляр на всё время работы приложения (ОПАСНО!)
// services.AddSingleton<HomeController>();

Ключевые моменты:

  • Контроллеры должны быть stateless. Любые данные, сохраняемые между запросами, должны храниться во внешнем хранилище (база данных, кэш, сессия).
  • Зависимости контроллера (сервисы) инжектируются через конструктор и разрешаются в соответствии со своим временем жизни (Transient, Scoped, Singleton).
  • Регистрация контроллера как Singleton приведет к тому, что все его поля (и инжектированные Scoped-сервисы) будут общими для всех запросов, что вызовет race conditions и ошибки данных.

Ответ 18+ 🔞

А, ну это классика, блядь! Сейчас объясню на пальцах, без этих заумных терминов, от которых мозг вскипает.

Смотри, представь себе автомат с кофе. Каждый раз, когда ты подходишь и жмёшь кнопку, он для тебя новую чашку готовит. Не ту, которую предыдущий клиент недопи́л, а свежую, чистую. Вот так и ASP.NET с контроллерами работает по умолчанию: на каждый новый запрос — новый, свежий экземпляр контроллера. Старый — нахуй, в утиль. Это чтобы ты, мудак, не додумался в полях контроллера какую-то хуйню между запросами хранить, типа «а вот счетчик запросов заведу приватный». Потому что если два человека одновременно придут, у них race condition случится, и всё ебнется. А так — изоляция полная, каждый в своём песочнице копается.

А если очень хочется по-другому? Ну, если ты упоротый и тебе неймётся, можно, конечно, в контейнере зависимостей поковыряться. Там, в Program.cs, где сервисы регистрируют.

// Вот это дефолтное поведение — один экземпляр на запрос (Scoped). Оно же и так работает.
services.AddScoped<HomeController>();

// А вот это — ОПАСНОСТЬ, МАТЬ ТВОЮ! Один экземпляр на ВСЁ приложение (Singleton).
// services.AddSingleton<HomeController>();

Запомни раз и навсегда: Контроллеры должны быть без состояния, как монахи, блядь. Всё, что нужно запомнить между запросами — тащи в нормальное хранилище: базу данных, кэш, сессию (хотя сессия тоже та ещё шляпа). Если сделаешь контроллер синглтоном, то все его поля, и даже те сервисы, которые должны жить только в рамках одного запроса (Scoped), станут общими для всех. Представь, что твой DbContext начнёт тащить данные от Ивана для запроса Пети — вот тогда и будет тебе настоящий пиздец и race conditions на ровном месте.

Короче, не выёбывайся, оставь как есть. Умные люди не просто так это придумали.