渐渐地,我开始研究 IoC 和所有这些美食,现在我只是想不通如何根据 MVVM 的规则在 WPF 应用程序中使用它们。
假设我做了一些容器设置(我使用 Autofac):
class ContainerConfig
{
public static IContainer Configure()
{
ISettings settings = new ConfigurationBuilder<ISettings>().UseJsonFile("Settings.json").Build();
var builder = new ContainerBuilder();
builder.RegisterType<MainViewModel>().SingleInstance();
builder.RegisterInstance(settings).SingleInstance();
return builder.Build();
}
}
到目前为止,我在其中注册了 2 个对象:
MainViewModel
- 应用程序的主虚拟机,据我了解,它应该在一个实例中。- 一些应用程序设置对象。
接下来,我覆盖OnStartup
,为了创建一个窗口,设置它DataContext
并显示所有这些:
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
var container = ContainerConfig.Configure();
using var scope = container.BeginLifetimeScope();
var mainViewModel = scope.Resolve<MainViewModel>();
new MainWindow() { DataContext = mainViewModel }.Show();
}
}
看来,当我朝着正确的方向前进时,或者不是?
好的,接下来要测试工作,我将通过写入MainViewModel
以下内容来简单地绑定设置中的属性:
class MainViewModel
{
public ISettings Settings { get; }
public MainViewModel(ISettings settingsModel)
{
Settings = settingsModel;
}
}
以及绑定本身:
<TextBlock Text="{Binding Settings.SomeValue}"/>
一切似乎都正常,一切都很好。
现在假设我需要创建另一个 VM,例如,它需要一个主 VM,我这样做:
class SecondViewModel
{
private MainViewModel main;
public SecondViewModel(MainViewModel mainViewModel)
{
main = mainViewModel;
}
public int Test { get; set; } = 33;
private void SomeMethod()
{
main.SomeProperty = false;
}
}
我注册它:
builder.RegisterType<SecondViewModel>().SingleInstance();
好吧,我添加到MainViewModel
新的虚拟机:
class MainViewModel
{
public ISettings Settings { get; }
public SecondViewModel Second { get; }
public MainViewModel(ISettings settingsModel, SecondViewModel second)
{
Settings = settingsModel;
Second = second;
}
}
结果,我得到一个循环错误,然后我清楚地明白我做错了什么。
在搜索了一些信息后,我找到了一种解决方法。
简而言之,正如您所看到的,我不太了解这一切应该如何工作,并且出现了很多问题,例如:
- 我做对了吗?
- 容器中应该记录什么?
- VM 层是否需要接口?
- 如何不破坏 MVVM?
一般来说,帮助我弄清楚如何在 WPF 应用程序中正确实现 IoC,甚至使用 MVVM?
DI 容器是一种工具,可以为您完成您可以在不违反任何原则的情况下自己完成的工作。
如您所知,容器是一种为您创建对象并跟踪其生命周期的东西。
在您的代码中,您在构造函数中创建了 2 个具有循环依赖关系的类,这使得无法创建此类类(除非将 NULL 传递给构造函数)。尝试手动创建类,你会发现你会失败,因为创建类A需要提供类B,而创建类B需要提供类A。这个循环称为循环依赖,并非如此很容易克服它。
但是,正如我所说,克服它并不容易,但是有可能,例如,如果您为类定义一个工厂并将工厂传递给构造函数。然后,只有当您不尝试直接在构造函数内部访问工厂时,它才会起作用。
例如
IBFactory 实现可能是最简单的
如果我没记错的话,一些容器本身就支持创建这样的工厂。在这种情况下,懒惰的程序员,而不是工厂,只是将整个容器转发给 A 类,这很丑陋并且是一种反模式 - 服务定位器。
但是,我还想说,理想情况下,从属模型想要了解主要模型的这种情况不应该存在。努力设计尽可能自给自足的虚拟机。如果虚拟机之间必须进行通信,那么在一般情况下有一个消息总线,或者您可以转发您的一些接口,但通常这种通信应该受到限制。虚拟机之间的对话越少,调试的麻烦就越少。
现在让我们来回答问题:
我们的职业没有什么是对的,但我会以不同的方式解决你的问题。
因此,第二个模型不知道任何主模型,并且主模型对从属模型的反应逻辑存储在一个地方 - 在主模型中。
无论您打算使用什么。您的问题不在容器中,而是在您的类的组织中 - 在循环依赖中。
取决于你的班级组织。有时它是有道理的,有时它没有(更多时候它没有)。在您的情况下,接口不会帮助您。
您在这里仅使用 VM 层进行操作,因为无论您如何制作该层,它本身都不会违反 MVVM。