Читаю "Внедрение зависимостей на платформе .NET 2-е издание" (на русском, вышла весной). Автор явно несколько раз пишет -
При использовании DI-контейнера корень композиции должен быть единствен-ным местом, где используется этот контейнер. Применение DI-контейнера вне корня композиции приводит к возникновению антипаттерна «Локатор сервисов» (Service Locator), который будет рассмотрен в следующей главе.
Приводит примеры, показывает что это некрасиво. Хорошо, предположим, я согласен.
Есть у меня WPF (или любое другое) десктоп приложение. Каждое нажатие какой то "кнопки" в UI я считаю действием в отдельном скопе. Т.е. часть API должно создавать соответствующие по жизни экземпляры. Условно, в обработчике ICommand.Execute такой псевдокод:
void ICommand.Execute(object parameter)
{
using (DI.CreateSomeScope())
new SomeCommand(...).Execute(parameter);
}
Вопроса два, первый важный, второй в комментах разобрали, но может у кого есть своё видение вопроса - делитесь тоже:
- каким образом я могу тут объявить скоп? Скоп согласно книге и большинству DI фреймворков - вполне себе ответственность контейнера. Тащить его сюда - явный сервис локатор.
- И что делать с
new SomeCommand? Ну т.е. у меня например уже запущено главное окно программы, отобразилось всё что мне хочется. И тут пользователь нажимает "Настройки". Мне надо создать VM настроек, чтобы потом присвоить в забинженное свойство, для изменения приложения. А за создание нестабильных (вм вполне подходит под нестабильные) зависимостей отвечает опять DI. Не очень понимаю, каким образом в середине жизненного цикла приложения создавать экземпляры объектов. В книге автор показывает в основном или элементарные примеры, или аспнет, в котором есть заранее продуманная точка - создание контроллера для обработки запроса.
каким образом я могу тут объявить скоп- это задается при регистрации типа, контейнер сам следит за тем, когда надо создавать новый объект, а когда дать уже готовый, вам надо лишь попросить его через DI (обычно конструктор).Мне надо создать VM настроек- создает/отдает за вас контейнер, если тип помечен как синглтон, то будет один объект на все время работы приложения, если это простая регистрация - объект каждый раз новый, а если это Scope, то объект будет существовать скажем так, всю "цепочку вызовов". – EvgeniyZ May 09 '21 at 16:36SomeCommand. Созданную фабрику можно хранить как поле класса. Это уберет необходимость делатьnew SomeCommand. Для использованию using достаточно, что быscopeреализовывалIDisposable/IAsyncDisposable. – Sergey Skvortsov May 09 '21 at 16:37в котором есть заранее продуманная точка- в том же WPF или где либо еще есть также эта точка, например в WPF вы инициализируете контейнер, а потом из него только один раз при старте забираете главное окно и его VM, все, дальше контейнер все типы и все остальное инициализирует за вас. Вообще, если идет разговор про WPF, то посмотрите реализацию Prism, он весь основан на DI контейнерах. – EvgeniyZ May 09 '21 at 16:37Как объявить сам скоп?- вам не надо ничего объявлять, повторю, это все берет на себя контейнер. Регистрируете в нем тип, указываете его как Scope и все, он будет существовать если, очень грубо говоря примерно так, то есть при инициализацииSomeClass, для которого требуетсяLoggerиSecondClass(который тоже требуетLogger), контейнер сделает один объектLoggerи внедрит их вSomeClassиSecondClass. Следующий раз, когда вы повторно попроситеSomeClass, у васLoggerбудет уже другой. – EvgeniyZ May 09 '21 at 16:50SomeCommand должен быть тогда не вызовом конструктора, а полем вьюшки?- Что такоеSomeCommand?ICommand? Если да, то почему это вообще должно быть в контейнере? Если это отдельная логика, то через конструктор просите и дальше уже либо ссылку на него в теле класса держите, либо используете лишь конструктор.вся система будет создана на открытии главного окна- при первом обращение. Если у вас длинная цепочка зависимостей, то да, будут сразу, если у вас открытие нового окна по клику, то объект будет создан при клике и забыт после закрытия. – EvgeniyZ May 09 '21 at 16:53в UI могут потребоваться новые объекты- Не забывайте, что речь идет проDI, внедрение зависимостей, а они как внедряются? Либо через конструктор, либо через свойство. В вашем псевдокодеnew SomeCommand(...)это не DI. Да, вы можете взять контейнер, через конструктор, и в любом месте написатьvar obj = container.Resolve<SomeType>(), вам это не мешает кто-либо сделать. Вы главное поймите что такое Scope. Когда вы просите корневой объект, то все зависимости для него, которые помечены как Scope, будут иметь один экземпляр и при создание нового корневого, будут и новые объекты. – EvgeniyZ May 09 '21 at 17:07