Search code examples
mvvmdependency-injectioninversion-of-controlservice-locator

Avoiding the service locator with inversion of control while dynamically creating objects


I have a WPF application based on MVVM with Caliburn.Micro and Ninject. I have a root viewmodel called ShellViewModel. It has a couple of dependencies (injected via constructor) which are configured in Caliburn's Bootstrapper. So far so good.

Somewhere down the line, there is a MenuViewModel with a couple of buttons, that in turn open other viewmodels with their own dependencies. These viewmodels are not created during creation of the root object, but I still want to inject dependencies into them from my IoC container.

I've read this question on service locator vs dependency injection and I understand the points being made.

I'm under the impression however that my MenuViewModel needs to be able to access my IoC container in order the properly inject the viewmodels that are being made dynamically..which is something I'm trying to avoid. Is there another way?


Solution

  • Yes, I believe you can do something a bit better.

    Consider that if there was no on-demand requirement then obviously you could make those viewmodels be dependencies of MenuViewModel and so on up the chain until you get to the root of the object graph (the ShellViewModel) and the container would wire everything up.

    You can put a "firewall" in the object graph by substituting something that can construct the dependencies of MenuViewModel for the dependencies themselves. The container is the obvious choice for this job, and IMHO from a practical standpoint this is a good enough solution even if it's not as pure.

    But you can also substitute a special-purpose factory instead of the container; this factory would take a dependency on the container and provide read-only properties for the real dependencies of MenuViewModel. Accessing the properties would result in having the container resolve the objects and returning them (accessor methods would also work instead of properties; what's more appropriate is another discussion entirely, so just use whatever you think is better).

    It may look like that you haven't really changed the status quo, but the situation is not the same it would be if MenuViewModel took a direct dependency on the container. In that case you would have no idea what the real dependencies of MenuViewModel are by looking at its public interface, while now you would see that there's a dependency on something like

    interface IMenuViewModelDependencyFactory
    {
        public RealDependencyA { get; }
        public RealDependencyB { get; }
    }
    

    which is much more informative. And if you look at the public interface of the concrete MenuViewModelDependencyFactory things are also much better:

    class MenuViewModelDependencyFactory : IMenuViewModelDependencyFactory
    {
        private Container container;
    
        public MenuViewModelDependencyFactory(Container container) { ... }
    
        public RealDependencyA { get { ... } }
        public RealDependencyB { get { ... } }
    }
    

    There should be no confusion over what MenuViewModelDependencyFactory intends to do with the container here because it's so very highly specialized.