Prism 7 - 将 IContainer 对象注入视图模型

Rob*_*win 4 prism unity-container mvvm prism-7

我最近有机会创建一个新的基于棱镜的应用程序。我用6.3版本已经有一段时间了,但是看到prism 7已经退出预发布,想试一试。我使用 Prism 模板包创建了一个新的棱镜应用程序,并且一切都按预期开箱即用。我更新了视图模型,就像在 6.3 中通常做的那样,传入容器,这样我就可以解析一些稍后会向视图提供信息的对象,在 6.3 中,我将执行以下操作:

public MainWindowViewModel(IRegionManager aRegionManager,
                           IUnityContainer aUnityContainer) : base()
Run Code Online (Sandbox Code Playgroud)

现在在 7.1.0.431 中,我尝试做同样的事情,但更新了接口以考虑新的 IOC 抽象。

public MainWindowViewModel(IRegionManager aRegionManager,
                           IContainerProvider aContainerProvider,
                           IContainerRegistry aContainerRegistry) : base()
Run Code Online (Sandbox Code Playgroud)

这会从 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 
Run Code Online (Sandbox Code Playgroud)

这就像我缺少一个引用,但我正在将该类型传递到应用程序的 RegisterTypes 调用中,因此应该找到所有引用。我对新的 7.X 版本做错了吗?

编辑:每@mnistic

这是模板包提供的 App.xaml.cs 中的代码,其中传入了 IContainerRegistry。

  protected override void RegisterTypes(IContainerRegistry containerRegistry)
  {
      //containerRegistry is a valid instance here
  }
Run Code Online (Sandbox Code Playgroud)

更新

再深入一点,传递给 RegisterTypes 的 IContainerRegistry 列出了调用该方法时可用的所有类型/接口。它注册了一个 IUnityContainer 实例。我在创建项目时为 IOC 选择了 Unity,但我假设 IContainerRegistry 将客户端从实际实现中隐藏了,这可能是错误的。如果我更新 ViewModel 构造函数以接收 IUnityContainer 的对象,它会正确解析。

public MainWindowViewModel(IRegionManager aRegionManager,
                           IUnityContainer aContainerProvider) : base()
Run Code Online (Sandbox Code Playgroud)

这是理想的行为吗?

Hau*_*ger 6

不要这样做。您不希望将容器放在解析根目录之外,测试很糟糕,隐藏了其他明显的依赖关系并且根本没有任何好处。

如果需要服务,直接注入即可。如果您需要工厂,请注入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 )
         {
             // ...
         }
     }
}
Run Code Online (Sandbox Code Playgroud)

如果Device没有someParameter,你会跳过IFactoryDeviceFactory只注入一个Func<IProduct>...... Unity 会注意每个Device接收它的IServicethen。

请记住 - 容器是为了简化事情:它解决依赖关系并创建实例并管理单例。但是如果你没有容器,一切仍然可以正常工作,就像你的单元测试一样。您只需要手动创建所有依赖项。

回到手头的话题 - 这IContainerRegistry只是一个短暂的、薄的包装器IUnityContainer(在你的情况下),所以注册代码在使用不同容器的不同应用程序中看起来有些相似。Prism 试图通过注册来将您推向正确的方向(见上文),IContainerRegistry以便您在应该使用它的地方使用它(在模块初始化期间)并阻止您在其他地方使用它(通过使其无法注入它) )。