将依赖注入添加到 MVVM 应用程序
Adding Dependency Injection to an MVVM application
尝试使用 MVVM 模式回填 WPF 应用程序以使用依赖项注入。我对 DI 不是很熟悉,以前只用过一次,但我想我理解其中的原理。
我需要确保所有绑定都在一个地方注册 - 应用程序根目录。在 WPF 中,这是 OnStartup 方法。因此,我抓取 Ninject 并将其放入我的应用程序中以尝试自动将我的存储库 class 绑定到初始视图:
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 加载到 window:
NavigationHelper.NewWindow(this, new QuoteViewModel(quote, new Repository()));
这行代码正是让我首先进入 DI 的原因 - 我无法对此进行测试,因为我无法在此处模拟出依赖关系。在这种情况下添加 DI 对我一点帮助都没有,因为我只应该在 OnStartUp 中使用我的 IoC 容器,所以我不能使用 kernel.Get 来获取我的 QuoteViewModel,对吗?
四处窥探所以我看到很多人建议我使用服务定位器解决这个问题。这对我来说是新的,我也看到很多人告诉我,将它用于 DI 是一种反模式,不应该被 bargepole 触及。谁是对的?
也许更重要的是,是否有解决此问题的巧妙方法?我见过几个其他示例,这些示例需要不同包的大杂烩才能使其正常工作。现在,感觉 MVVM 和 DI 不能很好地相互配合。
你快到了。你错过了两件事:
子视图模型的工厂
您需要一个能够为您创建子 VM 的工厂。为这个工厂引入一个接口,这样你就可以在测试中替换它。
public interface IVmFactory {
IQuoteViewModel CreateQuoteViewModel();
}
你可以自己实现这个接口or let NInject do it for you。
务必在 DI 容器中注册此工厂,以便容器在收到实例化 类 请求时能够解析它,该工厂作为依赖项。
ViewModels.MainViewModel
中的正确 DI
现在,您可以使用标准构造函数注入将 IVmFactory
注入视图模型:
public class MainViewModel {
public MainViewModel(IVmFactory vmFactory) {
_vmFactory = vmFactory;
}
// ...
}
DI 和 MVVM
一旦您找到解决 WPF 默认构造函数问题的方法(当您尝试让 WPF 实例化 VM 时),DI 和 MVVM 可以很好地协同工作。
DI 显然不是反模式,but service locator is。不幸的是,服务定位器通常与 MVVM 一起推荐,因为它使您能够快速入门,而无需创建工厂并正确注入它们。权衡是快速开始与拥有干净且可测试的设计 - 自己决定。
我同意服务定位器是一种反模式,我个人通过为 StandardKernel(注入器)创建一个包装器 class 来解决这个问题,它实现了一个接口(IInjector),然后将其自身注入 classes 通过字段注入需要它:
[Inject] public IInjector Injector {get; set;}
void MyClassMethod()
{
var instance = this.Injector.Get<ISomeInterface>();
// etc
}
这消除了服务定位反模式,同时也抽象出 DI 框架的具体实现细节。
尝试使用 MVVM 模式回填 WPF 应用程序以使用依赖项注入。我对 DI 不是很熟悉,以前只用过一次,但我想我理解其中的原理。
我需要确保所有绑定都在一个地方注册 - 应用程序根目录。在 WPF 中,这是 OnStartup 方法。因此,我抓取 Ninject 并将其放入我的应用程序中以尝试自动将我的存储库 class 绑定到初始视图:
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 加载到 window:
NavigationHelper.NewWindow(this, new QuoteViewModel(quote, new Repository()));
这行代码正是让我首先进入 DI 的原因 - 我无法对此进行测试,因为我无法在此处模拟出依赖关系。在这种情况下添加 DI 对我一点帮助都没有,因为我只应该在 OnStartUp 中使用我的 IoC 容器,所以我不能使用 kernel.Get 来获取我的 QuoteViewModel,对吗?
四处窥探所以我看到很多人建议我使用服务定位器解决这个问题。这对我来说是新的,我也看到很多人告诉我,将它用于 DI 是一种反模式,不应该被 bargepole 触及。谁是对的?
也许更重要的是,是否有解决此问题的巧妙方法?我见过几个其他示例,这些示例需要不同包的大杂烩才能使其正常工作。现在,感觉 MVVM 和 DI 不能很好地相互配合。
你快到了。你错过了两件事:
子视图模型的工厂
您需要一个能够为您创建子 VM 的工厂。为这个工厂引入一个接口,这样你就可以在测试中替换它。
public interface IVmFactory {
IQuoteViewModel CreateQuoteViewModel();
}
你可以自己实现这个接口or let NInject do it for you。
务必在 DI 容器中注册此工厂,以便容器在收到实例化 类 请求时能够解析它,该工厂作为依赖项。
ViewModels.MainViewModel
中的正确 DI
现在,您可以使用标准构造函数注入将 IVmFactory
注入视图模型:
public class MainViewModel {
public MainViewModel(IVmFactory vmFactory) {
_vmFactory = vmFactory;
}
// ...
}
DI 和 MVVM
一旦您找到解决 WPF 默认构造函数问题的方法(当您尝试让 WPF 实例化 VM 时),DI 和 MVVM 可以很好地协同工作。
DI 显然不是反模式,but service locator is。不幸的是,服务定位器通常与 MVVM 一起推荐,因为它使您能够快速入门,而无需创建工厂并正确注入它们。权衡是快速开始与拥有干净且可测试的设计 - 自己决定。
我同意服务定位器是一种反模式,我个人通过为 StandardKernel(注入器)创建一个包装器 class 来解决这个问题,它实现了一个接口(IInjector),然后将其自身注入 classes 通过字段注入需要它:
[Inject] public IInjector Injector {get; set;}
void MyClassMethod()
{
var instance = this.Injector.Get<ISomeInterface>();
// etc
}
这消除了服务定位反模式,同时也抽象出 DI 框架的具体实现细节。