Внедрение Зависимостей в ASP.NET Core. Продолжение (2/4)
Начало
Время жизни сервисов
Контейнер DI отвечает за создание и уничтожение экземпляров сервисов. Доступно 3 варианта регистрации:
1. Transient – новый экземпляр сервиса создаётся каждый раз, когда он требуется. Каждый класс, зависящий от такого сервиса, получит отдельный экземпляр:
services.AddTransient<IEmailSender, EmailSender>();
- самый простой в работе: если срок жизни сервиса сложно определить, Transient сервис чаще всего будет наиболее подходящим;
- не требует потокобезопасности, т.к. не подходит для сохранения состояния;
- потенциально может неэффективно использовать память и вести к созданию и уничтожению множества объектов.
2. Singleton – экземпляр создаётся на всё время работы приложения. Один и тот же экземпляр сервиса будет использоваться во всех зависящих от него классах всё время:
services.AddSingleton<ILoggerFactory, LoggerFactory>();
- изменение состояния сервиса (если оно допускается) должно быть потокобезопасно;
- обычно более эффективен, чем Transient, т.к. создаётся только один объект, поэтому подходит для дорогих в создании объектов,
- может приводить к утечкам памяти при регистрации больших редко используемых объектов;
- подходит для объектов с общими функциями, не сохраняющими состояние (например, логгер).
3. Scoped – экземпляр сервиса создаётся на время обработки запроса к приложению. Зависимые объекты в течение обработки одного запроса получат один и тот же экземпляр. Например, в EntityFramework класс
DbContext регистрируется как Scoped:services.AddScoped<IOrderService, OrderService>();
Избегайте захвата сервисов
Сервис не должен зависеть от другого сервиса с меньшим временем жизни! Если Singleton-сервис зависит от Transient- или Scoped-сервиса, то последний может быть захвачен и не будет уничтожен в течение всего времени жизни приложения. Это может приводить как к утечкам памяти, так и к совместному использованию непотокобезопасного кода из Transient-сервисов.
Начиная с ASP.NET Core 2.0 по умолчанию включена валидация времени жизни сервисов (
ValidateScopes). Она приводит к ошибке времени выполнения при старте приложения, если будет обнаружен захват сервисов. Однако, учтите, что эта функция отключена в продакшн, и её не рекомендуется включать вне среды разработки.Регистрация открытых обобщённых типов
Иногда требуется зарегистрировать реализацию обобщённого интерфейса. Например,
ILogger<T>. В этом случае используется необобщённый метод Add… и typeof:services.AddSingleton(
typeof(ILogger<>), typeof(MyLogger<>));
Если параметров типа больше одного, внутрь угловых скобок добавляются запятые(например,
typeof(ISomeInterface<,>)).Дескрипторы сервисов
Дескрипторы сервисов (класс
ServiceDescriptor) содержат информацию о регистрируемых сервисах и используются внутри контейнера DI. var sd = new ServiceDescriptor(typeof(IOrderService),
typeof(OrderService), ServiceLifetime.Scoped);
services.Add(sd);
Чаще всего создание дескриптора скрыто в методах расширения, описанных выше, и создание его вручную не требуется.
Продолжение следует…
Источник: https://app.pluralsight.com/library/courses/aspdotnet-core-dependency-injection/