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 так что вы используете его там, где вы должны его использовать (во время инициализации модуля), и не позволяет использовать его где-либо еще (делая невозможным его внедрение).

Другие вопросы по тегам