views:

220

answers:

2

Hi, 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..."

A: 

StructureMap is likely performing Setter Injection where it will populate public settable properties on an object it's resolving. According to the documentation,

By default, all public "Setters" are optional, meaning that these setters will only be set if they are explicitly configured for a specific Instance

So have you by chance set up the properties to be auto wired? If so, you'll still have the circular dependency issue.

Edit: I see that you have. In your instance because B has A[] injected, StructureMap must resolve each A's dependency for a B that needs A[], and so on...

statenjason
IIRC, I tried the same thing in Castle Windsor. It did the property resolution after resolving component's constructors, which meant that the circular dependency was effectively broken. Assuming I wasn't dreaming this fact, I was wondering if StructureMap had a similar facility?
Andy
There's an example of circular dependency solutions with Windsor and StructureMap here: http://bit.ly/aZsr9c . I know it's a bit older but still may apply. I notice that it used `TheInstanceNamed("Processor1")` for the last SetterDependency's configuration, maybe that will help?
statenjason
Hi statenjason, many thanks for your help and the link. I gave it a go and still got the circular dependency. I've amended my question with a small code snippet to illustrate the issue.
Andy
+1  A: 

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.

Joshua Flanagan
Hi Joshua - I just gave it a go and got a "Bidirectional Dependency Problem" - I think this is a sign of a circular dependency in StructureMap? Is the construction and setup of an object by StructureMap effectively atomic, so circular references can't be resolved? It seems unlikely, but I've not been able to get it to work so far...
Andy
Bidirectional Dependency Problem does indicate a circular reference. However, the code sample I provided WILL work, as the instance of B is created before the OnCreation lambda is called. You can then pass that instance of B into A's constructor, and then set B's property to A. I've tested the code above and it works. If you are still getting circular reference problems, there must be other dependencies you haven't mentioned.
Joshua Flanagan
I should clarify that when using my code example, you should NOT set any policies to have StructureMap perform setter injection on B. You are manually doing the setter injection in the OnCreation lambda.
Joshua Flanagan
brilliant thanks Joshua
Andy