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部分的解决方案:
Subscribe 你的 ViewModels 使用 IMyService 到 MyServiceChangedEvent(首先你应该实现它)。在处理程序中,您可以通过 ServiceLocator 或 通过事件参数 更改实现,引发 PropertyChanged 以更新数据绑定并为您的案例执行其他操作。
public class MyServiceChangedEvent : CompositeWpfEvent<IMyService>
{
}
IMyService 上的 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");
我正在尝试在 运行 时为启动期间注册的类型更改具体类型。我能够更新容器和服务定位器。但是已经存在的视图模型仍然引用了引导的原始服务。
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部分的解决方案:
Subscribe 你的 ViewModels 使用 IMyService 到 MyServiceChangedEvent(首先你应该实现它)。在处理程序中,您可以通过 ServiceLocator 或 通过事件参数 更改实现,引发 PropertyChanged 以更新数据绑定并为您的案例执行其他操作。
public class MyServiceChangedEvent : CompositeWpfEvent<IMyService> { }
IMyService 上的 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");