
Я сделал простейшую утилиту для регистрации в контейнере по атрибутам на интерфейсе\реализации. Добавил тесты а так же поддержку IServiceCollection.
Зачем?
Убрать весь бойлерплейт регистраций в контейнере.
На больших проектах с регистрациями в контейнере иногда можно ошибиться даже при merge.
Если в проектах есть какие-нибудь IChainProcessor-ы в количестве более 5-10 штук - удобнее регистрировать их все через атрибут.
Ситуации с тем, что написал сервис(ы), но забыл зарегистрировать в контейнере теперь исключены.
[TypeRegistration(LifetimeManagementType.PerThread)]
internal interface I1
{
public void Action1();
public void Action2();
}
Теперь вы можете добавлять реализации, не добавляя их в контейнер вручную.
Для регистрации Mock в Debug конфигурациях можно (и нужно) использовать директивы препроцессора #if.
Так же вы можете переопределить LifetimeManagement для любых реализаций.
[DerivedTypeRegistration(LifetimeManagementType.Singletone)]
internal class C1 : I1
{
public void Action1()
{
var a = 1 + 2;
}
public void Action2()
{
var b = 3 + 4;
}
}
Самое смешное, что 2 атрибута обеспечивают все возможные типы регистрации в контейнере.
Как пользоваться?
Устанавливаем атрибуты регистрации
Добавить TypeRegistrationAttribute на интерфейс, реализации которого хочется регистрировать в контейнере.
Добавить DerivedTypeRegistrationAttribute на реализацию чтобы переопределить LifetimeManagement.
Запускаем регистрацию
Вызвать
servicesCollection.RegisterByAttributes();
Чтобы вытянуть регистрации из сборки с данными атрибутами достаточно всего-лишь вызвать
servicesCollection.RegisterByAttributes(myExternalAssembly);
Теперь все реализации зарегистрированы в контейнере.
А это протестированно?
Да, написано 14 тестов.
Тесты:
1 к 1
1 к N
1 к 1 переопределение Lifetime
1 к N переопределение Lifetime
Все это для:
1) простых интерфейсов с простыми реализациями
2) Generic интерфейсов с простыми реализациями
3) и Generic интерфейсов с Generic реализациями
4) Так же есть тесты на смешанные кейсы
Заключение
Теперь регистрацию в контейнере не нужно переписывать по 10 раз, достаточно указывать атрибуты.
Для импорта из сборок без атрибутов по прежнему придется использовать сторонние утилиты.
Исходники лежать здесь.
Upd
Добавил Nuget пакет.
Поправил регистрацию если интерфейсы в другой сборке.
Комментарии (6)
pankraty
08.08.2025 12:12А как быть, если класс реализует 2 интерфейса, и его нужно зарегистрировать как синглтон таким образом, чтобы при резолве любого из интерфейсов возвращался один и тот же экземпляр?
ValeriyPus Автор
08.08.2025 12:12Атрибуты на оба интерфейса
[TypeRegistration(LifetimeManagementType.Default)]
на класс
[DerivedTypeRegistration(LifetimeManagementType.Singletone)]
alex1t
А что будет если есть два класса, реализующий интерфейс. При обычной регистрации будет использоваться тот, что зарегистрирован последним. Или надо использовать named регистрации. А здесь какой порядок будет?
Такая декларативная регистрация, разбросанная по коду на мой взгляд не очень удобна, поскольку требует времени на поиск и идентификацию. Я предпочитаю иметь один класс/метод, в котором все регистрации контейнеров и производятся из корня композиции (обычно Startup.ConfigureServices)
ValeriyPus Автор
Тот класс, который зарегистрирован последним. Специально для Вас вышла версия 1.0.0.4. С поддержкой Keyed регистрации. Надо установить name в атрибуте для реализации.
Но на мой взгляд это плохая практика, у нас еще появляются списки имен, которые надо тащить в сервисы.
Скорее, наоборот. Ctrl+T, Открыл интерфейс/класс и смотри.
Регистрации в контейнерах тоже не удобно - по 700 строк бойлерплейта, тяжело мержить, добавил интерфейс\реализацию - иди пиши еще 50 строк, не пропусти ничего и т.д.
withkittens
Так они у вас никуда не делись, только теперь размазаны по всему солюшену.
ValeriyPus Автор
нет, читайте внимательнее.
98% регистраций - атрибут НА ИНТЕРФЕЙСЕ.
Т.е. строк точно меньше, чем при в регистрации в контейнере. Менее 1 строки на реализацию интерфейса.
Все не "размазано по солюшену", а берется из метаданных к интерфейсу\классу.
С тем же успехом можно сказать, что из-за модификаторов доступа теперь все размазано по солюшену, без единой таблицы импорта\экспорта :).
Размазано по солюшену - это именованные (Keyed) регистрации, и их получение из контейнера :). Вот где место скопления ошибок, не видимых при компиляции.