将DI容器使用保留在Silverlight和MVVM中的组合根目录中

我不太清楚我是如何设计的,所以我在Silverlight + MVVM应用程序的组合根目录中保留对DI容器的引用。

我有以下简单的使用场景:有一个主视图(可能是一个项目列表)和一个打开一个项目的编辑视图的操作。 因此,主视图必须在用户执行操作时创建并显示编辑视图(例如,单击某个按钮)。

为此,我有以下代码:

public interface IView { IViewModel ViewModel {get; set;} } 

然后,对于我需要能够创建的每个视图,我都有一个抽象工厂,就像这样

 public interface ISomeViewFactory { IView CreateView(); } 

然后将此工厂声明为“父”视图模型的依赖项,如下所示:

 public class SomeParentViewModel { public SomeParentViewModel(ISomeViewFactory viewFactory) { // store it } private void OnSomeUserAction() { IView view = viewFactory.CreateView(); dialogService.ShowDialog(view); } } 

所以一切都很好,直到这里,没有DI容器在视线:)。 现在来了ISomeViewFactory的实现:

 public class SomeViewFactory : ISomeViewFactory { public IView CreateView() { IView view = new SomeView(); view.ViewModel = ???? } } 

“????” 部分是我的问题,因为视图的视图模型需要从DI容器中解析,因此它会注入其依赖项。 我不知道的是,除了组合根之外,如果没有对DI容器的依赖,我怎么能做到这一点。

一种可能的解决方案是依赖于注入工厂的视图模型,如下所示:

 public class SomeViewFactory : ISomeViewFactory { public SomeViewFactory(ISomeViewModel viewModel) { // store it } public IView CreateView() { IView view = new SomeView(); view.ViewModel = viewModel; } } 

虽然这有效,但它有一个问题,因为整个对象图是“静态”连接的(即“父”视图模型将获得SomeViewFactory的实例,它将获得SomeViewModel的一个实例,并且这些实例将生存为“父”视图模型存在),注入的视图模型实现是有状态的,如果用户打开子视图两次,第二次视图模型将是同一个实例并具有之前的状态。 我想我可以通过“初始化”方法或类似方法解决这个问题,但它闻起来不太对劲。

另一个解决方案可能是包装DI容器并让工厂依赖于包装器,但它仍然是一个“伪装”的DI容器:)

ps:我目前的解决方案是工厂知道DI容器,只有它们和具有这种依赖关系的组合根。

为了尽可能接近您的示例代码,您可以以IViewPopulator的forms引入另一个间接层:

 public interface IViewPopulator { void Populate(IView view); } 

您现在可以像这样实现SomeViewFactory:

 public class SomeViewFactory : ISomeViewFactory { private readonly IViewPopulator populator; public SomeViewFactory(IViewPopulator populator) { if (populator == null) { throw new ArgumentNullException("populator"); } this.populator = populator; } public IView CreateView() { IView view = new SomeView(); this.populator.Populate(view); return view; } } 

这分离了视图的创建和ViewModel的数量,遵循单一责任原则 。 在某种程度上,它也是服务聚合的一个例子。

您现在可以将IViewPopulator实现为具有正常依赖关系的具体类型:

 public class SomeViewPopulator : IViewPopulator { private readonly IDependency dep; public SomeViewPopulator(IDependency dep) { if (dep == null) { throw new ArgumentNullException("dep"); } this.dep = dep; } public void Populate(IView view) { var vm = // Perhaps use this.dep to create an instance of IViewModel... view.ViewModel = vm; } } 

可能有其他方法可以模拟IView和IViewModel之间的关系,但上面代表了一种可能的解决方案。

关键是要不断提取抽象,直到每个人都有明确的责任。 这个练习实际上并不是要使代码与容器无关,而是最终要遵守SOLID原则。