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

4

最近我有机会创建一个新的基于棱镜的应用程序。我之前一直使用6.3版本,但看到prism 7已经不再是预发布状态,所以想试试。我使用Prism模板包创建了一个新的prism应用程序,一切都按预期工作。我像通常在6.3中那样更新了视图模型,传入Container以便解析某些对象,这些对象稍后会提供信息给视图。在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版本中,我做错了什么吗?

编辑:根据@mnistic的建议

这里是从提供的App.xaml.cs模板包中传递IContainerRegistry的代码。

  protected override void RegisterTypes(IContainerRegistry containerRegistry)
  {
      //containerRegistry is a valid instance here
  }
更新: 深入了解后,传递给 RegisterTypes 方法的 IContainerRegistry 列出了在调用该方法时可用的所有类型/接口。它已注册了一个 IUnityContainer 实例。当我创建项目时选择了 Unity 作为 IOC,但我可能错误地认为 IContainerRegistry 将客户端隐藏在实际实现之后。如果我将 ViewModel 构造函数更新为使用 IUnityContainer 对象,则可以正确解析。
public MainWindowViewModel(IRegionManager aRegionManager,
                           IUnityContainer aContainerProvider) : base()

这是期望的行为吗?

你说“类型被传递到RegisterTypes中”,但我们可能需要看一下那段代码。 - mnistic
@mnistic 很抱歉,我忘记了它,因为它是由VS模板包生成的项目提供的。我编辑了问题以包括您请求的数据。 - Rob Goodwin
1个回答

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 )
         {
             // ...
         }
     }
}

如果Device没有someParameter,您将跳过IFactoryDeviceFactory,只注入一个Func<IProduct>... Unity会确保每个Device都接收到它的IService
请记住-容器的存在是为了简化事情:它解析依赖关系、创建实例并管理单例。但是如果没有容器,一切仍然可以正常工作,就像在单元测试中一样。您只需要手动创建所有依赖项。
回到手头的话题-IContainerRegistry只是IUnityContainer(在您的情况下)周围短暂而薄弱的包装,以便在使用不同容器的不同应用程序中注册代码看起来有些相似。Prism试图通过注册IContainerRegistry来将您推向正确的方向(请参见上文),以便您在模块初始化期间使用它,并防止您在其他地方使用它(通过使其无法注入)。

1
感谢您的回复。因此,在未来某个时候,如果您想解决所有已由不同模块注册的实现IFoo类型的实例的情况。您将创建一个IFoo工厂并进行注册?IFooFactory如何解决已注册的所有对象而不获取对容器的处理?我认为我误解了如何处理“不要这样做”的评论。 - Rob Goodwin
1
我在我的编辑中尝试回答了你的问题。至于“不要这样做” - 字面上理解,就是不要注入容器 :-) - Haukinger
我相信我现在理解了你的陈述。这不是我经常做的事情,只有在初始化后我想获取所有已注册的时间类型对象的情况下才会这样做。但我认为根据你的回答和编辑,我可以消除这种情况。感谢你抽出时间来帮助我! - Rob Goodwin
“不要这样做” 很好地总结了主题,但随后的解释是一个很好的补充!阅读愉快。 - Joel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接