Question

I have two classes which I need to reference each other.

class Foo
{
    public Foo(IBar bar) {}
}

class Bar
{
    public Bar(IFoo foo) {}
}

When I do:

container.RegisterAutoWiredAs<Foo, IFoo>();
container.RegisterAutoWiredAs<Bar, IBar>();

and when I try to resolve either interface I get a circular dependency graph which results in an infinite loop. Is there an easy way to solve this in Funq or do you know of a workaround?

Was it helpful?

Solution

You can always (and in all containers, I'd say) rely on Lazy as a dependency instead, and that would yield the desired result. In Funq:

public Bar(Lazy<IFoo> foo) ...
public Foo(Lazy<IBar> bar) ...

container.Register<IBar>(c => new Bar(c.LazyResolve<IFoo>());
container.Register<IFoo>(c => new Foo(c.LazyResolve<IBar>());

OTHER TIPS

The answer to your question is no, there is no easy way. Given the code above, it is impossible to construct either class without Funq, so there's no reason to expect Func to be able to do it.

var foo = new Foo(/* what do I pass here? */);
var bar = new Bar(foo);

Of course, if you had another implementation of either IFoo or IBar without the dependency, or you refactored, it might be possible.

In general, the answer to the question "how do I break up circular references when doing Dependency Injection" is: "use property injection".

class Foo
{
    public Foo() {}

    // Break the dependency cycle by promoting IBar to a property.
    public IBar Bar { get; set; }
}

class Bar
{
    public Bar(IFoo foo) {}
}

With Funq I think this would be the way to register this dependency.

container.Register<IBar>(c =>
{
    var foo = new Foo();
    var bar = new Bar(foo);
    foo.Bar = bar;
    return bar;
});

Furthermore, I agree with Tim Rogers' comment. When you have a circular dependency, there is probably a problem in your design, and you should take a look at it. This is not always wrong, but often is. However, the code you show is very abstract, and there is no way for us to give any feedback on that.

This works for me:

using Funq;
using NUnit.Framework;

namespace FunqIoCyclicReferenceTest
{
    [TestFixture]
    public class FunqIoCCyclicReferenceTest
    {
        [Test]
        public void Please_Work()
        {
            var container = new Container();
            container.Register<IBar>(c => new Bar());
            container.Register<IFoo>(c => new Foo(c.Resolve<IBar>()));

            var foo = container.Resolve<IFoo>();

            Assert.IsNotNull(foo);
        }
    }

    public class Foo : IFoo
    {
        public Foo(IBar bar)
        {
            bar.Foo = this;
            Bar = bar;
        }

        public IBar Bar { get; set; }
    }

    public interface IBar
    {
        IFoo Foo { get; set; }
    }

    public interface IFoo
    {
        IBar Bar { get; set; }
    }

    public class Bar : IBar
    {
        public IFoo Foo { get; set; }
    }
}

EDIT
Same idea but without side-effects in constructor:

// interfaces
public interface IBar
{
    IFoo Foo { get; set; }
}

public interface IFoo
{
    IBar Bar { get; set; }
}

// implementations
public class Foo : IFoo
{
    public IBar Bar { get; set; }
}    

public class Bar : IBar
{
    public IFoo Foo { get; set; }
}

// usage
container.Register<IBar>(c => new Bar());
container.Register<IFoo>(c => 
{
    var bar = c.Resolve<IBar>();
    var foo = new Foo();

    bar.Foo = foo;
    foo.Bar = bar;
});

p.s. but I do agree with Tim Rogers - circular reference is a problem to solve.

After registering your types in the container, make the container available as a static variable:

public static class ContainerHolder
{
   public static Container Container {get;set;}
}

public class Foo : IFoo
{
  private IBar _bar;

  public Foo(IBar bar)
  {
    _bar = bar;
  }
}

public class Bar : IBar
{
  private IFoo _foo
  {
    get { return ContainerHolder.Container.Resolve<IFoo>(); }
  }

  public Bar()
  {
  }

}

I had a similar scenario and the dependency between the two classes made me realize they should actually be combined into a single class.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top