views:

291

answers:

3

Hi!

This question is about Unity Container but I guess it is applicable to any dependency container.

I have two classes with circular dependencies:

class FirstClass
{
    [Dependency]
    public SecondClass Second { get; set; }
}

class SecondClass
{
    public readonly FirstClass First;

    public SecondClass(FirstClass first)
    {
        First = first;
    }
}

Technically it's possible to instantiate and correctly inject dependencies for both of them if treat them as singletons:

var firstObj = new FirstClass();
var secondObj = new SecondClass(firstObj);
firstObj.Second = secondObj;

When I try to do the same with Unity, I get StackOverflowException:

var container = new UnityContainer();
container.RegisterType<FirstClass>(new ContainerControlledLifetimeManager());
container.RegisterType<SecondClass>(new ContainerControlledLifetimeManager());

var first = container.Resolve<FirstClass>(); // StackOverflowException here!
var second = container.Resolve<SecondClass>(); // StackOverflowException here too!

I understand that Unity tries to protect me from using partially initialized objects but I want to have this protection as an option, not an obligation.

Question: is current behavior disabable?

+2  A: 

I think you cannot use circular dependencies with unity at all.

See: http://msdn.microsoft.com/en-us/library/cc440934.aspx

Mischa
I don't like that Unity can't instantiate classes but I can do it manually.From MDSN link you gave, none of the following is true for my case: -Objects generated through constructor injection that reference each other in their constructor parameters -Objects generated through constructor injection where an instance of a class is passed as a parameter to its own constructor -Objects generated through method call injection that reference each other -Objects generated through property (setter) injection that reference each other
Konstantin Spirin
Problem is that for a few moments, "FirstClass" lives without the dependency-object, which means it's not really valid. All this basically depends on what your objects do when constructed, if your second class there would try to call into the first one, where the first one then expected the dependent object to be available, then it would fail. Circular chains like this is problematic, for several reasons, so you should try to avoid, if possible.
Lasse V. Karlsen
I'd prefer to get NRE rather than StackOverflowException, especially taking into account that I can instantiate my objects manually.
Konstantin Spirin
+1  A: 

One way round this would be to use lazy loading for the dependencies on one of the classes:

[TestFixture]
public class CircularUnityTest
{
    IUnityContainer container;

    [SetUp]
    public void SetUp()
    {
        container = new UnityContainer();
        container.RegisterType(typeof(ILazy<>), typeof(Lazy<>));
        container.RegisterType<FirstClass>(new ContainerControlledLifetimeManager());
        container.RegisterType<SecondClass>(new ContainerControlledLifetimeManager());
    }

    [Test]
    public void CanResolveFirstClass()
    {
        var first = container.Resolve<FirstClass>();
        Assert.IsNotNull(first);
    }

    [Test]
    public void CanResolveSecondClass()
    {
        var second = container.Resolve<SecondClass>();
        Assert.IsNotNull(second);
    }

    [Test]
    public void CanGetFirstFromSecond()
    {
        var second = container.Resolve<SecondClass>();
        Assert.IsNotNull(second.First);
    }
}

class FirstClass 
{
    [Dependency]
    public SecondClass Second { get; set; }
}

class SecondClass
{
    private readonly ILazy<FirstClass> lazyFirst;

    public FirstClass First { get { return lazyFirst.Resolve(); } }

    public SecondClass(ILazy<FirstClass> lazyFirst)
    {
        this.lazyFirst = lazyFirst;
    }
}

public interface ILazy<T>
{
    T Resolve();
}

public class Lazy<T> : ILazy<T>
{
    IUnityContainer container;

    public Lazy(IUnityContainer container)
    {
        this.container = container;
    }

    public T Resolve()
    {
        return container.Resolve<T>();
    }
}
Mark Heath
That's an interesting idea! What I don't like about it is that my classes will have to know about Unity and I have to use Lazy<T> instead of simple and clear references.
Konstantin Spirin
A: 

Hi,

you can use RegisterInstance instead of RegisterType to achieve your goal. It will behave just like singleton - will use the same instance every time Resolve is invoked. Take a look at this example:

class FirstClass
{
    [Dependency]
    public SecondClass Second { get; set; }
}

class SecondClass
{
    public readonly FirstClass First;

    public SecondClass(FirstClass first)
    {
        First = first;
    }
}

class Program
{
    static void Main(string[] args)
    {
        IUnityContainer container = new UnityContainer();
        var firstObj = new FirstClass();

        var secondObj = new SecondClass(firstObj);
        firstObj.Second = secondObj;

        // Register instance instead of type!!!
        container.RegisterInstance<FirstClass>(firstObj);
        container.RegisterType<SecondClass>();

        var first = container.Resolve<FirstClass>();
        var second = container.Resolve<SecondClass>(); 
    }
}

Cheers,

Pavel

Pavel Nikolov
You are manually creating both objects. Is my question that's what I said I don't want to do. My main objective make Unity do it for me. It will become much uglier in real-world application when you'll have dozens of components, not just two. Also in your sample *first.Second* and *second.First* will not be the same!
Konstantin Spirin