Недавно в этом посте вы познакомились с библиотекой Pure.DI. Этот пакет с анализатором/генератором кода .NET 5 задумывался как помощник, который пишет простой код для композиции объектов в стиле чистого DI, используя подсказки для построения графа зависимостей. Он следит за изменениями, анализирует типы и зависимости между ними, подсвечивает проблемы и предлагает пути решения. Важно отметить, что библиотека Pure.DI - это не контейнер внедрения зависимостей, в её задачи входит:
-
анализ графа зависимостей
-
определение в нем проблем и путей их решения
-
создание эффективного кода для композиции объектов
По обсуждениям в предыдущем посте у меня сложилось впечатление, что необходимо решить следующие вопросы:
-
добавить возможность использовать Pure.DI в инфраструктуре ASP.NET
-
убрать бинарную зависимость на API из пакета Pure.DI.Contracts
-
увеличить производительность для случаев, когда операция
Resolve()
выполняется многократно
Сейчас, после небольших доработок анализатор кода автоматически определяет, является ли проект ASP.NET проектом, и генерирует код специального метода расширения, который обеспечивает интеграцию с ASP.NET. Для демонстрации возможности выбрано серверное Blazor приложение:
Описание графа зависимостей находится в этом классе:
DI.Setup() .Bind<IDispatcher>().As(Singleton).To<Dispatcher>() .Bind<IClockViewModel>().To<ClockViewModel>() .Bind<ITimer>().As(Scoped).To(_ => new Timer(TimeSpan.FromSeconds(1))) .Bind<IClock>().As(ContainerSingleton).To<SystemClock>();
Для того чтобы связать эти DI типы с инфраструктурой ASP.NET нужно добавить всего лишь одну строку вызова метода расширения:
services.AddClockDomain();
Как видно из примера, его название зависит от имени класса-владельца, чтобы не путаться если их несколько. Помимо метода расширения, добавлены два времени жизни:
-
ContainerSingleton - чтобы использовать один объект типа на ASP.NET контейнер
-
Scoped - чтобы использовать по одному объекту типа на каждый ASP.NET scope
Сейчас их нельзя использовать вне контекста ASP.NET, иначе появится ошибка компиляции с информацией об этом.
Для решения вопроса о нежелательной бинарной зависимости на API я удалил пакет Pure.DI.Contracts. Теперь весь API для описания графа зависимостей генерируются на месте и является частью инфраструктурного кода проекта, где этот API и используется. Как итог, в проекты не добавляется ни одной бинарной зависимости, а единственная зависимость типа analyzers на пакет Pure.DI будет использована только во время компиляции и забыта сразу после. И, конечно, ее можно использовать без ограничения в любых проектах, не опасаясь зависеть от чего-то лишнего.
ASP.NET инфраструктура вызывает метод
Resolve()
для каждого запроса. Чтобы уменьшить
накладные расходы на этот вызов, был оптимизирован код,
ответственный за сопоставление типа корневого элемента композиции
объектов к методу создания этой композиции. С результатами
сравнительных тестов можно ознакомиться здесь. Хотелось бы
подчеркнуть, что в этом сравнении
используется спорный способ получения показателей
производительности. Поэтому, эти результаты, дают приблизительную
оценку накладных расходов на многократный вызов метода
Resolve()
.
Как обычно, любые конструктивные замечания и идеи очень приветствуются.