we're quite new to DI and we're refactoring our application to use Prism/Unity. We're almost there but got stuck on a circular dependency. I've been reading a lot, there are a lot of similar questions but I'm in doubt as to what would be a 'correct' solution in our situation.
The application is similar to some IDEs. We have a project explorer, search, documents, … The documents are actually treeviews with nodes of INodeViewModel. Note that the code samples are minimized versions of the real code.
We have a IDockingService
that manages the tool and document items.
public interface IDockingService
{
INodeViewModelNavigationService NodeViewModelNavigationService { get; }
ExplorerViewModel ExplorerViewModel { get; }
SearchViewModel SearchViewModel { get; }
ReadOnlyCollection<ToolItemViewModel> ToolItemViewModels { get; }
ReadOnlyCollection<DocumentItemViewModel> DocumentItemViewModels { get; }
}
And we have a ISpecificationViewModelService
that manages all the INodeViewModels and has the connection with the model.
public interface ISpecificationViewModelService
{
INodeViewModel RootX { get; }
INodeViewModel RootY { get; }
INodeViewModel RootZ { get; }
}
We have two requirements that conflict.
- When a new node is created somewhere we want to navigate to it. Currently we pass
IDockingService
via constructor injection to
the concreteSpecificationViewModelService
implementation and further down to each NodeViewModel. - Certain tools in the
DockingService
need to know about theISpecificationViewModelService
. For example the Explorer needs to display all the roots.
Since DockingItemService
creates and manages the toolitems such as the Explorer it needs the ISpecificationViewModelService
. But the ISpecificationViewModelService
needs the IDockingService
to be able to navigate to a node. Circular troubles.
Our first attempt didn't have this issue because we let the the tool items be created in the composition root but that didn't seem correct. Instances we not managed together nicely.
From reading around I understand that a factory (or another third class) could help out here. I'm not seeing how yet. I think I understand that the problem is that DockingService
only needs SpecificationViewModelService
to create the toolitems and vice versa. A factory could take this circular trouble away but I'm not sure it is a correct solution. The container can only be used in the composition root but wouldn't a facory (which needs the container?) then just be hiding the container and take over its work under a different name?
What would be a correct way to handle this problem?
Best Answer
I'm going to significantly abstract from your situation. I'm not sure if this will be the best solution to your problem. It will be a solution, but it may be just enabling a poor design. It's hard to tell, but certainly the situation comes up in reasonable designs.
It's quite common that you get in a situation that to build
X
you needY
, and to buildY
you needX
. Of course, it can't be that conceptually to buildX
you need to have completely built aY
and vice versa as then it would be logically impossible to construct the system. Instead, what is really meant is to build one you merely need a way of referring to the other. There are many ways of referring to something that hasn't yet been created. For example, in C you might pass a pointer to allocated but uninitialized memory, or you might pass a string that will be used to look up the created object in a registry. In a language like Haskell or Scala, one way to solve this is with lazy evaluation. Implementation-wise, this is more or less equivalent to passing around a higher order function with some internal mutable state, or, equivalently, a "factory" object that will memoize the dependency resolution. This is the approach I'm going to suggest here. I'll use Autofac's terminology and API below, though the idea should be easily be adaptable to other dependency injection frameworks.Here
IContainer
is intended to be the dependency injection container, andResolve
does the dependency look-up. I strongly recommend against passing the dependency injection container to objects, but in this case I'm viewingLazy
as part of the dependency injection framework. In fact, you should make sure that your dependency injection framework doesn't already provide such functionality. The alternative would be to make a factory for each class you want to lazily inject. The code would be basically identical except thatIContainer
would be replaced by any dependencies and instead of usingResolve
to create the object you'd usenew
. Alternatively, you could generalizeLazy
to take anAction<T>
and just memoize it and then these factory objects would just be singletons that you register in the composition root like:container.Register(new Lazy<Foo>(() => new Foo()));
The ideal situation would be to register the open generic typeLazy<>
, so that with one registration you could handle all cases. (I have verified that you can make this work with Autofac.)The upshot of this is that you would depend on
Lazy<IFoo>
instead ofIFoo
and you would access via theValue
property (but not in the constructor!)