Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unity resolving cyclic dependency

While learning Unity (DI framework in C#) I came across a situation where one class has a setter injection of ClassB

class ClassA : IClassA
{
    [Dependency]
    public IClassB ClassB
    {
        get { return _classB; }
        set
        {
            if (value == null) throw new ArgumentNullException("value");
            _classB = value;
        }
    }

and the other has a constructor injection of the ClassA

class ClassB : IClassB
{
    [InjectionConstructor]
    public ClassB(IClassA classA)
    {
        _classA = classA;
    }
}

I am not able to resolve both the classes correctly within the container.

var container = new UnityContainer();
container.RegisterType<IClassB, ClassB>();
container.RegisterType<IClassA, ClassA>();
IClassA classA = new ClassA();
var instance = container.Resolve<ClassA>();
instance.DoSomethingFromClassB();

log.Info("Constructor Injection");
var instanceB = container.Resolve<ClassB>();
instanceB.DoSomethingFromClassA();

This gives me a stack overflow exception

I tried different ordering of resolving this but it doesn't seem to work.

I this doable or am I just wasting my time.

What is exactly happening here ?

like image 660
jtkSource Avatar asked Sep 04 '14 15:09

jtkSource


People also ask

How can cyclic dependencies be resolved?

To resolve circular dependencies: Then there are three strategies you can use: Look for small pieces of code that can be moved from one project to the other. Look for code that both libraries depend on and move that code into a new shared library. Combine projectA and projectB into one library.

How do you break a circular dependency?

But circular dependencies in software are solvable because the dependencies are always self-imposed by the developers. To break the circle, all you have to do is break one of the links. One option might simply be to come up with another way to produce one of the dependencies, in order to bootstrap the process.

How can cyclic dependencies be prevented?

Circular dependencies can be introduced when implementing callback functionality. This can be avoided by applying design patterns like the observer pattern.

How does Spring overcome circular dependency?

A simple way to break the cycle is by telling Spring to initialize one of the beans lazily. So, instead of fully initializing the bean, it will create a proxy to inject it into the other bean. The injected bean will only be fully created when it's first needed.


2 Answers

The way that DI frameworks like Unity work is, when you call them to instantiate a class, they recursively instantiate all classes being passed into the constructor (or set by property) of that class. Those classes follow the same function, and so you can see how you've created an infinite loop of recursion. How does Unity construct A when it needs B and B when it needs A? Neither can ever be constructed.

You can't resolve co-dependent classes in most DI frameworks. This is a bad design pattern AKA a code smell. In the classical sense, if ClassA needs to know about ClassB, and ClassB in return needs to know about ClassA, then the reality is that they share concerns and should be combined into a single class ClassC. You gain nothing by having them in 2 separate classes since there is no separation of concerns in this case.

DI such as Unity is used to promote the pattern of Inversion of Control, which works only when classes have a one-way dependency (don't need to know about each other).

like image 94
Haney Avatar answered Oct 31 '22 17:10

Haney


I agree with @Haney that this is a code smell, but it is technically possible...

Just change one of the referenced types to be resolved via Lazy<T>. Then it does not actually resolve that type until it is used which will break out of the infinite recursion loop.

i.e.

class ClassB : IClassB
{
...
    [InjectionConstructor]
    public ClassB(Lazy<IClassA> classA)
    {
        _classA = classA;
    }
}
like image 21
TylerOhlsen Avatar answered Oct 31 '22 19:10

TylerOhlsen