6

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?

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
Konstantin Spirin
  • 20,609
  • 15
  • 72
  • 90

3 Answers3

5

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

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

Mischa
  • 893
  • 5
  • 7
  • 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 Sep 04 '09 at 07:09
  • 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 Sep 04 '09 at 07:12
  • I'd prefer to get NRE rather than StackOverflowException, especially taking into account that I can instantiate my objects manually. – Konstantin Spirin Sep 04 '09 at 08:01
2

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
  • 48,273
  • 29
  • 137
  • 194
  • 2
    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 instead of simple and clear references. – Konstantin Spirin Sep 17 '09 at 13:47
  • Sorry to step in on this two years later, but... This is exactly what I'm looking for, but it's not working for me. I get a dependency resolution exception, saying it cannot resolve `ILazy`. Does the code above still work, or have there been changes to Unity that require a different approach? – Samo Oct 03 '11 at 15:33
  • @Samo yes still working for me against Unity 2. What version of Unity are you using? Did you copy my code exactly? You need to register the open generic of ILazy for it to work. (n.b. I wrote this before .NET 4 came with its own Lazy type which is slightly different) – Mark Heath Oct 04 '11 at 10:16
  • In fact, it is even easier with Unity 2. Don't bother with the Lazy and ILazy, and just use a Func in the constructor of SecondClass and Unity will provide a Func that resolves from the container when it is called. – Mark Heath Oct 04 '11 at 10:20
  • @Mark Heath: no, I didn't copy all of your example verbatim, as I was trying to get it to work with classes specific to my domain, but I did copy your `ILazy<>` interface and your `Lazy<>` class and I registered them in the container the same way you did. When Unity tried to resolve my class with constructor injection, it complained that `ILazy` could not be resolved. I will try your `Func` example to see if it works for me. We are using Unity 2. Thanks for your response. – Samo Oct 04 '11 at 17:34
  • @Mark Heath: I get the same activation errors when I try using `Func` in the constructor :( – Samo Oct 11 '11 at 16:56
  • @Mark Heath: It's been suggested to me that an extension is necessary to make this work, but I can't seem to find it (nor can the dev who suggested it). Do you know if an extension is required? If so, can you point me to it? Thanks! – Samo Oct 25 '11 at 19:52
  • @Samo, no extension required. Create a new project, use Nuget to add Unity 2.0.151, then add NUnit and the tests above will pass. – Mark Heath Oct 26 '11 at 09:26
1

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
  • 9,401
  • 5
  • 43
  • 55
  • 1
    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 Sep 18 '09 at 14:20