Prism 7 - внедрение объектов IContainer в модель представления
Недавно у меня была возможность создать новое приложение на основе призмы. Я довольно давно использовал версию 6.3, но увидел, что призма 7 вышла из предварительной версии и хотела попробовать. Я создал новое приложение Prism, используя пакет шаблонов Prism, и все работало как положено из коробки. Я обновил модель представления, как это обычно делается в 6.3, для передачи в Контейнер, чтобы я мог разрешить некоторые объекты, которые позже предоставят информацию для представления, в 6.3 я бы сделал следующее:
public MainWindowViewModel(IRegionManager aRegionManager,
IUnityContainer aUnityContainer) : base()
Теперь в 7.1.0.431 я попытался сделать то же самое, но обновил интерфейсы, чтобы учесть новую абстракцию IOC.
public MainWindowViewModel(IRegionManager aRegionManager,
IContainerProvider aContainerProvider,
IContainerRegistry aContainerRegistry) : base()
Это создает исключение из ViewModelLocator.AutoWireViewModel для параметров IContainerX.
System.Exception {Unity.Exceptions.ResolutionFailedException}
{"Resolution of the dependency failed, type = 'Sample.ViewModels.MainWindowViewModel', name = '(none)'.\nException occurred while: while resolving.\nException is: InvalidOperationException - The current type, Prism.Ioc.IContainerProvider, is an interface and cannot be constructed. Are you missing a type
Это действует так, как будто мне не хватает ссылки, но этот тип передается в вызов RegisterTypes приложения, поэтому все ссылки должны быть найдены. Я делаю что-то не так для новой версии 7.X?
РЕДАКТИРОВАТЬ: Per @mnistic
Вот код из пакета шаблонов, предоставленного App.xaml.cs, в который передается IContainerRegistry.
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
//containerRegistry is a valid instance here
}
Обновление:
Немного подробнее, IContainerRegistry, который был передан в RegisterTypes, перечисляет все типы / интерфейсы, которые были доступны на момент вызова метода. Он имеет зарегистрированный экземпляр IUnityContainer. Я выбрал Unity для IOC, когда создавал проект, но я предположил, возможно, неправильно, что IContainerRegistry скрывал клиентов от фактической реализации. Если я обновлю конструктор ViewModel, чтобы получить объект IUnityContainer, он разрешится правильно.
public MainWindowViewModel(IRegionManager aRegionManager,
IUnityContainer aContainerProvider) : base()
Это желаемое поведение?
1 ответ
Не делайте этого. Вы не хотите, чтобы контейнер находился за пределами корня вашего разрешения, это ужасно тестировать, скрывать в противном случае очевидные зависимости и не приносит никакой пользы.
Если вам нужны услуги, введите их напрямую. Если вам нужны фабрики, введите Func<IProduct>
или же IHandcraftedFactory
, Если вам нужны все зарегистрированные типы, которые реализуют ISomething
залить ISomething[]
или же IEnumerable<ISomething>
,
Пример (комплекс) фабрики с продукцией:
public interface IFactory
{
IProduct CreateProduct( int someParameter );
}
internal class DeviceFactory : IFactory
{
public DeviceFactory( IService service )
{
_service = service;
}
public IProduct CreateProduct( int someParameter ) => new Device( someParameter, _someService );
private readonly IService _service;
private class Device : IProduct
{
public Device( int someParameter, IService aDependency )
{
// ...
}
}
}
Если Device
не имел someParameter
ты бы пропустил IFactory
а также DeviceFactory
и просто ввести Func<IProduct>
... единство заботится о том, чтобы каждый Device
получает его IService
затем.
Помните - контейнер предназначен для упрощения вещей: он разрешает зависимости, создает экземпляры и управляет синглетонами. Но если бы у вас не было контейнера, все бы работало нормально, как в случае с вашими юнит-тестами. Вы просто должны создать все зависимости вручную.
Вернуться к теме под рукой - IContainerRegistry
это просто недолгая, тонкая обертка вокруг IUnityContainer
(в вашем случае), так что регистрационный код выглядит несколько одинаково в разных приложениях, использующих разные контейнеры. Призма пытается подтолкнуть вас в правильном направлении (см. Выше), не регистрируя IContainerRegistry
так что вы используете его там, где вы должны его использовать (во время инициализации модуля), и не позволяет использовать его где-либо еще (делая невозможным его внедрение).