Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Circular dependencies in StructureMap - can they be broken with property injection?

I've got the simplest kind of circular dependency in structuremap - class A relies on class B in its constructor, and class B relies on class A in its constructor. To break the dependency, I made class B take class A as a property, rather than a constructor argument, but structuremap still complains.

I've seen circular dependencies broken using this method in other DI frameworks - is this a problem with Structuremap or am I doing something wrong?

Edit: I should mention that class B's property is an array of class A instances, wired up like this:

x.For<IB>().Singleton().Use<B>().Setter(y => y.ArrayOfA).IsTheDefault();

Just to clarify, I want the following sequence of events to occur:

  • Construct an instance of B, "b"
  • Construct an instance of A, "a", injecting "b" into its constructor
  • Set "b.ArrayOfA" to ["a"]

And I want all this to happen using autowiring, if possible...

Edit 2: Here's a simplified example that uses explicit wiring up:

interface ILoader { }
interface ILoaderManager { }

class Loader : ILoader
{
    public Loader(ILoaderManager lm) { }
}
class LoaderManager : ILoaderManager
{
    public ILoader Loader { get; set; } // Was an array, but same circular dependency appears here
}

ObjectFactory.Configure
(
    x =>
    {
        x.For<ILoader>.Singleton().Use<Loader>();
        x.For<ILoaderManager>().Singleton().Use<LoaderManager>().OnCreation((c, a) => a.Loader = c.GetInstance<ILoader>());
    }
);

Validating the configuration causes "Bidirectional Dependency Problem detected with RequestedType: IocTest2.ILoader..."

like image 429
Andy Avatar asked May 06 '10 15:05

Andy


People also ask

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 do you break a circular dependency in Spring boot?

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.

Can constructor injection prevents circular dependencies?

If you are predominantly using constructor injections then it is possible to create circular dependencies in Spring.

How do you fix a circular dependency problem?

There are a couple of options to get rid of circular dependencies. For a longer chain, A -> B -> C -> D -> A , if one of the references is removed (for instance, the D -> A reference), the cyclic reference pattern is broken, as well. For simpler patterns, such as A -> B -> A , refactoring may be necessary.


2 Answers

StructureMap can handle bi-directional situation also with a workaround using Lazy resolution.

If you have a simple situation like ClassA that depends on ClassB and ClassB that depends of ClassA, then you can choose one of them and convert the dependency as a Lazy dependency. This way worked for me and that error never appeared again..

public class ClassA
{
    private readonly Lazy<IClassB> _classB;

    public ClassA(Lazy<IClassB> classB)
    {
        _classB = classB;
    }

    public IClassB ClassB => _classB.Value;
}

public class ClassB 
{
    public IClassA _classA { get; set; }

    public ClassB (IClassA classA)
    {
        _classA = classA;
    }
}

More info here: http://structuremap.github.io/the-container/lazy-resolution/

like image 96
Javier Avatar answered Sep 16 '22 11:09

Javier


The closest you can get is something like this:

x.For<IB>().Use<B>()
    .OnCreation((ctx, instance) =>
    {
        instance.ArrayOfA = new IA[] {new A(instance) };
    });

If A has other dependencies that you want to resolve from the container, you can retrieve them from ctx within the OnCreation lambda.

like image 21
Joshua Flanagan Avatar answered Sep 20 '22 11:09

Joshua Flanagan