如何用 MVVM 中的新服务替换已注册的服务?

How to replace a registered service with a new one in MVVM?

尝试从良好的旧 MVVM Light 迁移到 Windows 社区工具包。我们应该如何覆盖已注册的服务,即在运行时用另一个实现替换它?

例子

我有一个 UI 层,它基本上是一个 WPF 应用程序和一个 VM 层,它是一个 class 库。结构如下所示:

App (UI)
  MainWindow (window)
  WPFDialogService (implements IDialog interface)
Library (VM)
  IDialog (interface)
  ConsoleDialogService (implements IDialog interface)
  MainVM (backend of MainWindow)
  ViewModelLocator (static class the performs Ioc registrations)

ViewModelLocator 是中心枢纽,应该注册所有服务。默认情况下,它将 ConsoleDialogService 注册为默认对话服务。

static ViewModelLocator()
{
  services.AddSingleton<IDialogService>(new ConsoleDialogService())
    .AddSingleton<MainVM>();

  Ioc.Default.ConfigureServices(services.BuildServiceProvider());
}

但是 UI 层应该取消注册并稍后注入不同的服务 WPFDialogService(在 MainWindow 的构造函数中或 App class).

在 MVVM Light 中,我们有 RegisterUnregister 方法,我们可以使用它们轻松实现这一目标。但是在 WCT 中我没有看到任何等价物。我应该再打电话给 ConfigureServices() 吗?它将如何影响已注册的其他服务?有没有只替换单个服务注册而不影响其他的方法?

另外,我们如何通过 WCT 中的构造函数管理服务注入?

如果你想替换集合中的服务,你会遇到麻烦。您不能在运行时简单地 add/remove 来自 serviceCollection 的服务。您将需要创建新范围、重建服务提供者等。

我确实有一个更简单的方法可以解决这个问题。您可以做的是更改集合中现有服务的属性。与其尝试替换整个服务,不如替换包装器服务上的属性怎么样。这对您来说是可行的解决方案吗?

我已经为你写了一个例子,我试图让这个例子与你的代码保持一致:

Wrapper 类 和对话服务

public interface IDialogService
{

}

public class ConsoleDialogService : IDialogService
{

}

public class WPFDialogService : IDialogService
{

}

public interface IDialogServiceWrapper
{
    public IDialogService DialogService { get; set; }
}

public class DialogServiceWrapper : IDialogServiceWrapper
{
    public IDialogService DialogService { get; set; }
}

构建服务提供者

public static void ViewModelLocator()
{
    services.AddSingleton<IDialogServiceWrapper>(new DialogServiceWrapper())
      .AddSingleton<MainVM>();

    var container = services.BuildServiceProvider();
    var dialogWrapper = container.GetService<IDialogServiceWrapper>();
    dialogWrapper.DialogService = new ConsoleDialogService();
    Ioc.Default.ConfigureServices(services.BuildServiceProvider());
}

UI层

public class MyUiLayer
{
    public IDialogServiceWrapper _dialogServiceWrapper;
    public MyUiLayer(IDialogServiceWrapper dialogServiceWrapper)
    {
        _dialogServiceWrapper = dialogServiceWrapper;
    }

    public void SwitchContainer()
    {
        _dialogServiceWrapper.DialogService = new WPFDialogService();
    }
}

如果您需要我澄清任何事情,请告诉我。

编码愉快!

好的。我想我终于找到了自己的路。不确定这是否是 standard/recommended 方法,但它确实以优雅的方式解决了问题。

标准 MVVM Light 方法的问题在于 ViewModelLocator class 存在于 VM 级别,根据 MVVM 的定义,它无法访问视图或应用程序层。因此,无法在 ViewModelLocator 中注册那些级别的实现。作为一种补救措施,MVVM Light 提供了 registerunregister 设施,我们可以调用这些更高层来替换现有注册或引入新注册。

当 migrating/upgrading 到 Windows 社区工具包时,必须避免使用集中式 ViewModel Locator 的整个想法。服务注册必须在有权访问所有实现的最高级别(例如应用层)执行,以便 select 正确实现(基于驱动该 selection 的任何标准)。这将避免在以后 replacing/injecting 任何新注册的需要。

服务注入应该完全委托给服务容器。将您的服务依赖项作为构造函数参数引入,并让您的服务容器自动为您注入它们。无需集中式 ViewModel Locator.

这种方法不仅可以用于 VM 层,甚至可以用于您的 UI 组件(windows/dialogs/custom 控件 classes 等)。只需在您的服务容器中注册它们,将所需的依赖项添加到它们的构造函数中,然后每当您需要实例化一个对象时,只需调用服务容器的 GetService<T> 让它为您完成所有注入魔法。