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

adr*_* h. 13 c# silverlight dependency-injection inversion-of-control mvvm

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

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

为此,我有以下代码:

public interface IView
{
   IViewModel ViewModel {get; set;}
}
Run Code Online (Sandbox Code Playgroud)

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

public interface ISomeViewFactory
{
   IView CreateView();
}
Run Code Online (Sandbox Code Playgroud)

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

public class SomeParentViewModel
{
   public SomeParentViewModel(ISomeViewFactory viewFactory)
   {
       // store it
   }

   private void OnSomeUserAction()
   {
      IView view = viewFactory.CreateView();
      dialogService.ShowDialog(view);
   }       
} 
Run Code Online (Sandbox Code Playgroud)

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

public class SomeViewFactory : ISomeViewFactory
{
    public IView CreateView()
    {
        IView view = new SomeView();
        view.ViewModel = ????   
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

public class SomeViewFactory : ISomeViewFactory
{
    public SomeViewFactory(ISomeViewModel viewModel)
    { 
       // store it
    }

    public IView CreateView()
    {
        IView view = new SomeView();
        view.ViewModel = viewModel;
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

Mar*_*ann 7

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

public interface IViewPopulator
{
    void Populate(IView view);
}
Run Code Online (Sandbox Code Playgroud)

您现在可以像这样实现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;
    }
}
Run Code Online (Sandbox Code Playgroud)

这分离了视图的创建和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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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