День 1645. #ЗаметкиНаПолях
Scrutor в .NET
Сегодня рассмотрим библиотеку Scrutor, варианты её использования и что она предлагает для упрощения внедрения зависимостей, а также о реализации сквозных задач с использованием шаблона декоратора.
Пакет Scrutor улучшает код внедрения зависимостей, предоставляя набор расширений для встроенного контейнера зависимостей .NET, которые добавляют поддержку сканирования готовых сборок и декорирования типов.
Использование Scrutor для сканирования сборки
Scrutor помогает упростить наш код внедрения зависимостей за счёт динамического поиска типов внутри сборок и их регистрации во время выполнения. Используя сканирование сборки, мы можем частично автоматизировать наш код регистрации зависимостей. Кроме того, мы можем использовать сканирование сборки для создания расширяемых систем. С помощью сканирования мы можем создать программу, способную находить и загружать дополнительные модули во время выполнения.
builder.Services.Scan(s => s
.FromCallingAssembly()
.AddClasses(c =>
с.InNamespaces("MyApp.Services"))
.AsImplementedInterfaces()
);
Здесь мы вызываем метод расширения Scan() который выполнит сканирование сборки (в данном случае - той, из которой он вызван) и выберет сервисы для регистрации на основе предоставленного нами селектора (в данном случае из пространства имён "MyApp.Services"). Наконец, мы регистрируем выбранные нами типы в качестве подстановки для всех интерфейсов, которые они реализуют, вызывая AsImplementedInterfaces(), т.к. обычно наши зависимости представляют собой интерфейсы (типа IUserService), а не классы напрямую.
Используя широкий спектр методов расширения, которые предоставляет Scrutor, мы можем очень точно определить сервисы, которые мы хотим зарегистрировать, например, из другой сборки (библиотеки классов):
builder.Services.Scan(s => s
.FromAssemblyOf<ICustomerService>()
.AddClasses(c =>
c.AssignableTo<ICustomerService>())
.AsMatchingInterface());
Здесь мы просим Scrutor сканировать сборку, в которой находится интерфейс ICustomerService, используя метод расширения FromAssemblyOf<ICustomerService>(). Затем среди всех реализаций в этой сборке мы выбираем только те, которые могут быть назначены ICustomerService.
Работа с обобщёнными типами
Если мы хотим добавить реализацию обобщённого типа, то вместо обобщённой реализации метода AssignableTo, мы можем использовать метод с аргументом типа:
…
.AddClasses(c =>
c.AssignableTo(typeof(IService<>)))
…
Указание времени жизни зависимостей
Мы можем использовать спецификаторы времени жизни зависимостей:
builder.Services.Scan(s => s
…
.WithTransientLifetime()
);
Или соответственно WithScopedLifetime() и WithSingletonLifetime().
Обработка нескольких реализаций
Стратегия регистрации — это то, как Scrutor обрабатывает случаи, когда для одного и того же интерфейса существует несколько реализаций:
builder.Services.Scan(s => s
…
.UsingRegistrationStrategy(RegistrationStrategy.Skip)
.AsImplementedInterfaces()
);
В этом коде, используя метод UsingRegistrationStrategy() с параметром RegistrationStrategy.Skip, мы указываем Scrutor игнорировать дополнительные реализации любого интерфейса после регистрации первой. Другие варианты:
- RegistrationStrategy.Append - добавляет новую регистрацию для существующих сервисов.
- RegistrationStrategy.Throw - выдаёт ошибку при попытке зарегистрировать существующий сервис.
Окончание следует…
Источник: https://code-maze.com/dotnet-dependency-injection-with-scrutor/