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

Dependency injection 依赖注入:注入部分初始化的对象,dependency-injection,unity-container,circular-dependency,Dependency Injection,Unity Container,Circular Dependency,这个问题是关于Unity容器的,但我想它适用于任何依赖性容器 我有两个具有循环依赖关系的类: class FirstClass { [Dependency] public SecondClass Second { get; set; } } class SecondClass { public readonly FirstClass First; public SecondClass(FirstClass first) { First

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

我有两个具有循环依赖关系的类:

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!
var container=newunitycontainer();
RegisterType(新的ContainerControlledLifetimeManager());
RegisterType(新的ContainerControlledLifetimeManager());
var first=container.Resolve();//这里是StackOverflowException!
var second=container.Resolve();//这里也是StackOverflowException!
我知道Unity试图保护我不使用部分初始化的对象,但我希望将这种保护作为一种选择,而不是义务


问题:当前的行为是不可分解的吗?

我认为您根本不能使用统一的循环依赖关系

见:

解决这个问题的一种方法是对其中一个类的依赖项使用延迟加载:

[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>();
    }
}
[TestFixture]
公共类循环测试
{
i单元容器;
[设置]
公共作废设置()
{
容器=新的UnityContainer();
RegisterType(typeof(ILazy),typeof(Lazy));
RegisterType(新的ContainerControlledLifetimeManager());
RegisterType(新的ContainerControlledLifetimeManager());
}
[测试]
public-void-firstclass()
{
var first=container.Resolve();
Assert.IsNotNull(第一个);
}
[测试]
public void CanResolveSecondClass()
{
var second=container.Resolve();
Assert.IsNotNull(第二个);
}
[测试]
public void CanGetFirstFromSecond()
{
var second=container.Resolve();
Assert.IsNotNull(第二个。第一个);
}
}
头等舱
{
[依赖性]
公共第二类第二{get;set;}
}
二等舱
{
私人只读ILazy lazyFirst;
public FirstClass First{get{return lazyFirst.Resolve();}
公共二等舱(ILazy lazyFirst)
{
this.lazyFirst=lazyFirst;
}
}
公共接口ILazy
{
T解析();
}
公共课:ILazy
{
i单元容器;
公共容器(IUnityContainer容器)
{
this.container=容器;
}
公共事务解决方案
{
返回container.Resolve();
}
}

您可以使用RegisterInstance而不是RegisterType来实现您的目标。它的行为就像singleton一样——每次调用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>(); 
    }
}
class头等舱
{
[依赖性]
公共第二类第二{get;set;}
}
二等舱
{
公共只读第一类第一类;
公共二等舱(头等舱优先)
{
第一=第一;
}
}
班级计划
{
静态void Main(字符串[]参数)
{
IUnityContainer容器=新的UnityContainer();
var firstObj=新的第一类();
var secondObj=新的第二类(第一类);
firstObj.Second=secondObj;
//注册实例而不是类型!!!
容器注册状态(firstObj);
container.RegisterType();
var first=container.Resolve();
var second=container.Resolve();
}
}
干杯


Pavel

我不喜欢Unity不能实例化类,但我可以手动实现。从您提供的MDSN链接,对于我的情况,以下情况都不成立:-通过构造函数注入生成的对象在其构造函数参数中相互引用-通过构造函数注入生成的对象,其中类的实例作为参数传递给它自己的构造函数-通过方法调用注入生成的对象,每个对象都引用其他-通过相互引用的属性(setter)注入生成的对象问题是,“第一类”有时没有依赖对象,这意味着它不是真正有效的。所有这些基本上取决于对象在构造时的行为,如果第二个类尝试调用第一个类,而第一个类期望依赖对象可用,那么它将失败。像这样的循环链是有问题的,有几个原因,所以如果可能的话,你应该尽量避免。我更喜欢使用NRE而不是StackOverflowException,特别是考虑到我可以手动实例化我的对象。这是一个有趣的想法!我不喜欢的是,我的类必须了解Unity,我必须使用Lazy,而不是简单明了的引用。很抱歉两年后介入,但是。。。这正是我想要的,但对我来说不起作用。我得到一个依赖项解析异常,表示它无法解析
ILazy
。上面的代码是否仍然有效,或者是否对Unity进行了需要不同方法的更改?@Samo yes仍然为我对抗Unity 2。你使用的是什么版本的Unity?你完全复制了我的代码吗?您需要注册ILazy的开放泛型才能使其工作。(注意,在.NET4自带懒惰类型之前,我就写过这篇文章了,它有点不同)事实上,使用Unity2更容易。不要为Lazy和ILazy而烦恼,只要在SecondClass的构造函数中使用一个Func,Unity将提供一个在调用时从容器解析的Func。@Mark Heath:不,我没有逐字复制您的所有示例,因为我试图让它与特定于我的域的类一起工作,但是我确实复制了你的
ILazy
接口和你的
Lazy
类,并以与你相同的方式在容器中注册了它们。当Unity试图用构造函数注入来解析我的类时,它抱怨
ILazy
无法解析。我将尝试您的
Func
示例,看看它是否适合我。我们正在使用Unity 2。Th