向MVVM应用程序添加依赖项注入
本文关键字:注入 依赖 添加 MVVM 应用程序 | 更新日期: 2023-09-27 18:26:17
尝试使用MVVM模式回填WPF应用程序以使用依赖项注入。我对DI并不太熟悉,以前只使用过一次,但我想我理解其中的原则。
我需要确保绑定都在一个地方注册——应用程序根。在WPF中,这是OnStartup方法。因此,我抓住Ninject并将其放入我的应用程序中,尝试自动将我的存储库类绑定到初始视图:
private void OnStartup(object sender, StartupEventArgs e)
{
IKernel kernel = new StandardKernel();
kernel.Bind<IRepository>().To<Repository>();
Views.MainView view = new Views.MainView();
view.DataContext = kernel.Get<ViewModels.MainViewModel>();
view.Show();
}
从现在起,在中,我使用数据模板资源设置上下文:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:views="clr-namespace:My.Views"
xmlns:models="clr-namespace:My.ViewModels" >
<DataTemplate DataType="{x:Type models:MyViewModel}" >
<views:MyView />
</DataTemplate>
<!-- etc -->
</ResourceDictionary>
它是有效的。太棒了但是,在MainViewModel中,我按下一个按钮并将不同类型的ViewModel加载到窗口中:
NavigationHelper.NewWindow(this, new QuoteViewModel(quote, new Repository()));
这行代码正是我最初使用DI的原因——我无法测试它,因为我无法模拟这里的依赖关系。在这个实例中添加DI对我没有任何帮助,因为我只应该在OnStartUp中使用我的IoC容器,所以我不能使用内核。获取我的QuoteViewModel,对吧?
四处窥探,所以我看到很多人建议我使用服务定位器来解决这个问题。这对我来说是新的,我也看到很多人告诉我,将其用于DI是一种反模式,不应该用讨价还价来触及。谁是对的?
也许更重要的是,这个问题有一个巧妙的解决方案吗?我看到过其他几个例子,它们需要不同的套餐才能发挥作用。现在,感觉MVVM和DI只是不适合彼此打得很好。
您就快到了。你错过了两件事:
子视图模型的工厂
您需要一个能够为您创建子VM的工厂。为这个工厂引入一个接口,这样你就可以在测试中替换它。
public interface IVmFactory {
IQuoteViewModel CreateQuoteViewModel();
}
您可以自己实现这个接口,也可以让NInject。
请确保在DI容器中注册此工厂,以便容器在接收到实例化以工厂为依赖项的类的请求时能够解析它。
ViewModels.MainViewModel
中的正确DI
现在,您可以使用标准的构造函数注入将IVmFactory
注入到视图模型中:
public class MainViewModel {
public MainViewModel(IVmFactory vmFactory) {
_vmFactory = vmFactory;
}
// ...
}
DI和MVVM
一旦您找到了解决WPF默认构造函数问题的方法(当您尝试让WPF实例化VM时),DI和MVVM就可以很好地协同工作。
DI显然不是一种反模式,但服务定位器是。不幸的是,服务定位器通常被建议与MVVM一起使用,因为它使您能够快速启动,而不必创建工厂并正确注入它们。权衡是快速开始,而不是拥有一个干净的&可测试的设计-自己决定。
我同意服务定位器是一种反模式,我个人通过为StandardKernel(Injector)创建一个包装类来解决这个问题,该包装类实现了一个接口(IInjector),然后通过字段注入将自己注入到需要它的类中:
[Inject] public IInjector Injector {get; set;}
void MyClassMethod()
{
var instance = this.Injector.Get<ISomeInterface>();
// etc
}
这消除了服务位置反模式,还抽象了DI框架的特定于实现的细节。