6 Stimmen

Depedency Injection: Injektion von teilweise initialisierten Objekten

Diese Frage ist über Unity Container, aber ich denke, es ist für jede Abhängigkeit Container anwendbar.

Ich habe zwei Klassen mit zirkulären Abhängigkeiten:

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

class SecondClass
{
    public readonly FirstClass First;

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

Technisch ist es möglich, Abhängigkeiten für beide zu instanziieren und korrekt zu injizieren, wenn man sie als Singletons behandelt:

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

Wenn ich versuche, dasselbe mit Unity zu tun, erhalte ich 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!

Ich verstehe, dass Unity versucht, mich vor der Verwendung von teilweise initialisierten Objekten zu schützen, aber ich möchte diesen Schutz als Option haben, nicht als Verpflichtung.

Frage: Ist das derzeitige Verhalten untauglich?

5voto

Mischa Punkte 893

Ich denke, man kann mit Unity überhaupt keine zirkulären Abhängigkeiten verwenden.

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

2voto

Mark Heath Punkte 46572

Eine Möglichkeit, dies zu umgehen, wäre die Verwendung von "Lazy Loading" für die Abhängigkeiten von einer der Klassen:

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

1voto

Pavel Nikolov Punkte 8963

Können Sie RegisterInstance anstelle von RegisterType verwenden, um Ihr Ziel zu erreichen. Es verhält sich genau wie Singleton - es wird jedes Mal, wenn Resolve aufgerufen wird, dieselbe Instanz verwendet. Werfen Sie einen Blick auf dieses Beispiel:

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

Zum Wohl,

Pavel

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X