依赖注入:注入部分初始化的对象

时间:2009-09-04 06:52:29

标签: dependency-injection unity-container circular-dependency

这个问题是关于Unity Container的,但我想它适用于任何依赖容器。

我有两个循环依赖的类:

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

class SecondClass
{
    public readonly FirstClass First;

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

从技术上讲,如果将它们视为单例,可以实例化并正确地为它们注入依赖关系:

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

当我尝试使用Unity时,我得到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!

我理解Unity试图保护我不使用部分初始化的对象,但我希望将此保护作为一种选择,而不是义务。

问题:目前的行为是不可行的?

3 个答案:

答案 0 :(得分:5)

我认为你根本不能使用统一的循环依赖。

请参阅: http://msdn.microsoft.com/en-us/library/cc440934.aspx

答案 1 :(得分:2)

这样做的一种方法是对其中一个类使用延迟加载:

[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>();
    }
}

答案 2 :(得分:1)

您可以使用RegisterInstance而不是RegisterType来实现您的目标。它的行为就像单例一样 - 每次调用Resolve时都会使用相同的实例。看一下这个例子:

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>(); 
    }
}

干杯,

的Pavel