Prism / Unity 使用新的混凝土 class 更新 resolved/injected 引用

Prism / Unity update resolved/injected references with new concrete class

我正在尝试在 运行 时为启动期间注册的类型更改具体类型。我能够更新容器和服务定位器。但是已经存在的视图模型仍然引用了引导的原始服务。

Bootstrap代码:

container.RegisterInstance<IMyService>(new MyServiceA(), new ContainerControlledLifetimeManager());

视图模型代码:

public ViewModel(IMyService service)
{
    _service = service;
}

服务更改代码:

container.RegisterInstance<IMyService>(new MyServiceB(), new ContainerControlledLifetimeManager());

serviceLocator.Resolve returns 我的服务B。容器中不再存在 MyServiceA。但是现有的视图模型仍然引用 MyServiceA。

有什么方法可以更新容器更新 existing/resolved 引用吗?

更新: 也许这个单元测试可以帮助理解我正在战斗的行为。

    using Microsoft.VisualStudio.TestTools.UnitTesting;
    using Microsoft.Practices.Unity;

    namespace UnitTestProject1
    {
        [TestClass]
        public class UnitTest1
        {      
            [TestMethod]
            public void TestUnity()
            {
                // register ServiceA and ViewModel
                var container = new UnityContainer();
                container.RegisterType<IMyService, MyServiceA>();
                container.RegisterType<MyViewModel>();

                // resolve viewmodel
                var viewModel = container.Resolve<MyViewModel>();

                // replace ServiceA with ServiceB
                container.RegisterInstance<IMyService>(new MyServiceB());

                // Assert success, IMyService is MyServiceB
                Assert.AreEqual(container.Resolve<IMyService>().GetType(), typeof(MyServiceB));
                // Assert fails viewmodel still using MyServiceA
                Assert.AreEqual(viewModel.RegisteredService.GetType(), typeof(MyServiceB));
            }
        }    

        public interface IMyService
        {        
        }

        public class MyServiceA : IMyService
        {
        }

        public class MyServiceB : IMyService
        {
        }

        public class MyViewModel
        {
            public IMyService RegisteredService { get; }

            public MyViewModel(IMyService myService)
            {
                RegisteredService = myService;
            }


        }
    }

如果每次调用都执行 Proxy-class for IMyService which'll retrieve the actual implementation from ServiceLocator,问题将得到解决:

public class MyServiceProxy : IMyService
{
    public int DoWork(string someParameter)
    {
        return ServiceLocator.Resolve<IMyService>().DoWork(someParameter);
    }
}

现在您可以将 MyServiceProxy 注入到您的 class 构造函数中。另一种方法是为每个 IMyService 使用添加 ServiceLocator 的显式调用。但是您应该记住,ServiceLocator 会使您的代码更难理解,因为它隐藏了 class 依赖项。

更新

与检索到的数据绑定的情况提醒了对事件的反应。当事件 "Data source changed" 在您的系统中引发时,订阅者 (ViewModel) 应该处理此事件。它适用于 EventAggregator class which has implementation in Prism.

然后基于2部分的解决方案:

  1. Subscribe 你的 ViewModels 使用 IMyService 到 MyServiceChangedEvent(首先你应该实现它)。在处理程序中,您可以通过 ServiceLocator 或 通过事件参数 更改实现,引发 PropertyChanged 以更新数据绑定并为您的案例执行其他操作。

    public class MyServiceChangedEvent : CompositeWpfEvent<IMyService>
    {
    }
    
  2. IMyService 上的
  3. Publish new MyServiceChangedEvent 已更改。您可以在事件中创建新的 属性 以将新的实现传递给您的订阅者,而不是使用 ServiceLocator。

    var newMyService = new MyServiceB();
    container.RegisterInstance<IMyService>(newMyService, new ContainerControlledLifetimeManager());
    eventAggregator.GetEvent<MyServiceChangedEvent>().Publish(newMyService);
    

为什么不直接使用命名实例?

        _container.RegisterType<IMyService, MyService>("ServiceA", new ContainerControlledLifetimeManager());
        _container.RegisterType<IMyService, MyOtherService>("ServiceB", new ContainerControlledLifetimeManager());

        _container.Resolve<IMyService>("ServiceA");