Ответ
Для регистрации контекста базы данных (например, DbContext в Entity Framework Core) в ASP.NET Core наиболее правильным является тип времени жизни Scoped.
Почему Scoped?
- Безопасность: Один экземпляр
DbContextсоздается и используется в рамках одного HTTP-запроса (или другой области видимости). Это гарантирует, что операции в рамках одного запроса будут изолированы. - Производительность:
Scopedпредотвращает необходимость создавать новый контекст для каждой операции внутри запроса, но и не хранит его глобально, что могло бы привести к утечкам памяти или состоянию гонки. - Согласованность: Все репозитории и сервисы в рамках одного запроса работают с одним экземпляром контекста, что обеспечивает корректное отслеживание изменений (change tracking) и работу с транзакциями.
Пример регистрации:
// В Program.cs или Startup.cs
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")),
ServiceLifetime.Scoped // Явное указание (по умолчанию и так Scoped)
);
Важные исключения и нюансы:
TransientдляDbContext— плохая практика. Каждый запрос на создание сервиса получит новый контекст, что сломает отслеживание сущностей и может привести к множественным открытым соединениям.SingletonдляDbContext— категорически нельзя. Контекст не потокобезопасен, и его использование из разных потоков одновременно приведет к непредсказуемым ошибкам и повреждению данных.- Для не-веб-приложений (консольные, фоновые службы) область видимости (
Scoped) создается вручную с помощьюIServiceScopeFactory.
Ответ 18+ 🔞
Давай разберём эту тему про регистрацию контекста базы данных, а то народ постоянно наступает на одни и те же грабли, блядь.
Так вот, если ты в ASP.NET Core регистрируешь свой DbContext — ставь ему время жизни Scoped, и точка. Это не просто рекомендация, это, сука, правило железное.
А почему, спрашивается? Ну слушай сюда.
Во-первых, безопасность. Один запрос — один контекст. Всё, что происходит в рамках одного HTTP-запроса, работает с одной изолированной песочницей. Никаких случайных поделок сущностей из другого запроса, никакого неожиданного шаринга состояния. Красота, блядь.
Во-вторых, производительность. Не надо каждый раз, как обезьяна, создавать новый контекст на каждую мелкую операцию внутри запроса — он один и переиспользуется. Но при этом и не висит, как Singleton, до скончания времён, жрёт память и собирает в себя весь мусор приложения. Баланс, ёпта!
В-третьих, согласованность. Все твои репозитории и сервисы в рамках одного запроса тыкаются в один и тот же экземпляр. Change tracking работает как часы, транзакции — вообще красота. Хочешь откатить что-то — на раз-два.
Вот как это обычно выглядит в коде, смотри:
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")),
ServiceLifetime.Scoped // Это, кстати, можно даже не писать — оно там по умолчанию
);
Теперь про то, как НЕ НАДО, а то некоторые совсем еб... особо одарённые товарищи экспериментируют.
Transient — это пиздец, а не жизнь. Каждый раз, когда кто-то попросит DbContext, он получит свеженький, новый. Два репозитория в одном методе? Получат два разных контекста. Сущность из одного в другой не перетащишь, отслеживания изменений нет, соединений к базе может наоткрываться дофига. Полный атас, в общем.
Singleton — это уже не пиздец, а ядерный взрыв на твоей фабрике данных. Контекст НЕ потокобезопасен, чувак! Представь, что два параллельных запроса начинают в него что-то писать одновременно. Это как два мудака, пытающихся протолкнуться в одну дверь — в итоге оба упадут, и дверь сломается. Данные повредятся, исключения полетят такие, что мама не горюй. Никогда так не делай, я серьёзно.
А, ну и да, если ты пишешь не веб-приложение (консольку какую-нибудь или фоновую службу), то там просто так Scoped не возьмёшь. Там нет автоматического "запроса". Придётся, блядь, вручную создавать область видимости через IServiceScopeFactory. Но это уже отдельная история, про которую тоже надо знать, а то будешь сидеть и чесать репу.