问题
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?
回答1:
I think you cannot use circular dependencies with unity at all.
See: http://msdn.microsoft.com/en-us/library/cc440934.aspx
回答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>();
}
}
回答3:
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
来源:https://stackoverflow.com/questions/1377608/depedency-injection-injecting-partially-initialized-objects