robaudas
robaudas

Reputation: 1638

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

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

Answers (2)

user5420778
user5420778

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

Vadim Martynov
Vadim Martynov

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:

  1. 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>
    {
    }
    
  2. 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

Related Questions