Reputation: 1638
I'm trying to change the concrete type at run-time for a type registered during startup. I'm able to update the container & service locator. But already existing view models still have reference to the original service bootstrapped.
Bootstrap Code:
container.RegisterInstance<IMyService>(new MyServiceA(), new ContainerControlledLifetimeManager());
ViewModelCode:
public ViewModel(IMyService service)
{
_service = service;
}
Service Changing Code:
container.RegisterInstance<IMyService>(new MyServiceB(), new ContainerControlledLifetimeManager());
serviceLocator.Resolve returns MyServiceB. MyServiceA no longer exists in the container. But existing view models still have reference to MyServiceA.
Is there any way to have updates to the container update existing/resolved references?
UPDATE: Maybe this unit test can help understand the behavior I'm fighting.
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;
}
}
}
Upvotes: 2
Views: 724
Reputation:
Why don't you just use named instances?
_container.RegisterType<IMyService, MyService>("ServiceA", new ContainerControlledLifetimeManager());
_container.RegisterType<IMyService, MyOtherService>("ServiceB", new ContainerControlledLifetimeManager());
_container.Resolve<IMyService>("ServiceA");
Upvotes: 1
Reputation: 8892
If you'll implement Proxy-class for IMyService which'll retrieve the actual implementation from ServiceLocator per each call the problem will be solved:
public class MyServiceProxy : IMyService
{
public int DoWork(string someParameter)
{
return ServiceLocator.Resolve<IMyService>().DoWork(someParameter);
}
}
Now you can inject MyServiceProxy to your class constructor. The other way is to add an explicit call of ServiceLocator to each IMyService usage. But you should remember that ServiceLocator can make your code more complicated to understand because it hides class dependences.
UPDATE
Situation with bindings to the retrieved data reminds the reaction to the event. When event "Data source changed" raises in your system the subscribers (ViewModels) sholud handle this event. It's work for EventAggregator class which has implementation in Prism.
Then the solution based on 2 parts:
Subscribe your ViewModels which uses IMyService to MyServiceChangedEvent (first you should implement it). In the handlers you can change implementation via ServiceLocator or via event parameters, raise PropertyChanged for update databinding and do another actions for your cases.
public class MyServiceChangedEvent : CompositeWpfEvent<IMyService>
{
}
Publish new MyServiceChangedEvent on IMyService changed. You can create new property inside your event to pass new implementation to your subscribers instead of using ServiceLocator.
var newMyService = new MyServiceB();
container.RegisterInstance<IMyService>(newMyService, new ContainerControlledLifetimeManager());
eventAggregator.GetEvent<MyServiceChangedEvent>().Publish(newMyService);
Upvotes: 1