如何在Caliburn.Micro中使用导体的依赖注入

Nes*_*tor 17 c# dependency-injection caliburn.micro

我有时使用Caliburn.Micro来创建应用程序.

使用最简单的BootStrapper,我可以像这样使用IoC容器(SimpleContainer):

private SimpleContainer _container = new SimpleContainer();

protected override object GetInstance(Type serviceType, string key) {
    return _container.GetInstance(serviceType, key);
}

protected override IEnumerable<object> GetAllInstances(Type serviceType) {
    return _container.GetAllInstances(serviceType);
}

protected override void BuildUp(object instance) {
    _container.BuildUp(instance);
}
Run Code Online (Sandbox Code Playgroud)

所以在Configure方法中我可以像这样添加和注册我的ViewModel:

container.PerRequest<MyMainViewModel>();
Run Code Online (Sandbox Code Playgroud)

我的ViewModel的构造函数可以有一个参数,在请求时由IoC容器注入:

public MyMainViewModel(IWindowManager windowManager)
{
  //do the init
}
Run Code Online (Sandbox Code Playgroud)

当我打电话时,它按预期工作 DisplayRootViewFor<MyMainViewModel>()

但是,如果我打算创建更多逻辑并使用Conductor,会发生什么?

在这些例子中,作者使用一个简单的,无IoC的实现来"方便":

为了使这个样本尽可能简单,我甚至没有使用带引导程序的IoC容器.我们先来看看ShellViewModel.它继承自Conductor,实现如下:

public class ShellViewModel : Conductor<object> {
    public ShellViewModel() {
        ShowPageOne();
    }

    public void ShowPageOne() {
        ActivateItem(new PageOneViewModel());
    }

    public void ShowPageTwo() {
        ActivateItem(new PageTwoViewModel());
    }
}
Run Code Online (Sandbox Code Playgroud)

因此,他们实例化 ViewModel,而不是从IoC容器中请求实例.

在这种情况下,依赖注入的正确用法是什么?

我有另一个ViewModel,它有一个像这样的构造函数:

public MySecondViewModel(MyParamClass input)
{
  //do the work
}
Run Code Online (Sandbox Code Playgroud)

我应该像这样修改代码:

Configure方法中:

simpleContainer.PerRequest<MyParamClass>(); //How could it be different every time?
Run Code Online (Sandbox Code Playgroud)

在售票员:

public void ShowPageOne() 
{
   ActivateItem(IoC.Get<MySecondViewModel>());
}
Run Code Online (Sandbox Code Playgroud)

此外,这是允许的还是违反了DI的规则:

protected override object GetInstance(Type serviceType, string key) 
{
  if(serviceType==typeof(MySecondViewModel))
    return new MySecondViewModel(new MyParamClass(2));
  return _container.GetInstance(serviceType, key);
}
Run Code Online (Sandbox Code Playgroud)

我可以看到使用DI,ViewModel应该由IoC容器提供而不是手动创建(更不用说所需的参数 - 它在容器内).

那么你能否给出一些暗示如何用导体实现IoC模式?

Nko*_*osi 5

最简单、最直接的方法是遵循显式依赖原则

所以假设

public MySecondViewModel(MyParamClass input) {
  //do the work
}
Run Code Online (Sandbox Code Playgroud)

并且它及其依赖项已向容器注册,

simpleContainer.PerRequest<MyParamClass>();
simpleContainer.PerRequest<MySecondViewModel>();
Run Code Online (Sandbox Code Playgroud)

指挥MainViewModel者可以依赖于委托(工厂),该委托可用于在需要时解决依赖关系。

public class MainViewModel : Conductor<object> {
    //...
    private readonly Func<MySecondViewModel> mySecondViewModelFactory;

    public MyMainViewModel(IWindowManager windowManager, Func<MySecondViewModel> mySecondViewModelFactory) {
        this.mySecondViewModelFactory = mySecondViewModelFactory;
        //...do the init
    }

    public void ShowPageOne() {
        var item = mySecondViewModelFactory(); //invoke factory
        ActivateItem(item);
    }
}
Run Code Online (Sandbox Code Playgroud)

虽然没有正确记录,但允许以延迟解析/实例化注入依赖项的形式SimpleContainer注入工厂委托(源代码)。Func<TDependency>仅当实际需要时,您才可以利用该功能来解决依赖关系。


Yit*_*hak 2

解决方案

我相信最好的解决方案是通过一个知道如何创建我的子视图模型的工厂。父视图模型将调用工厂。


成就:

  • 仅在需要时实例化子视图模型(惰性)
  • 您可以从父视图模型和/或从注入传递参数
  • 您可以使用模拟工厂为父视图模型编写单元测试。这使您能够测试父视图模型创建了您的子视图模型,而无需真正创建它们。

编辑:感谢@Nkosi 的回答,有一种简单的方法可以使用 Caliburn.Micro 注入惰性视图模型(类似工厂):) 使用我的答案进行此注入以获得最佳结果。