6

To pytanie dotyczy Unity Container, ale myślę, że ma ono zastosowanie do dowolnego kontenera zależności.Wstrzyknięcie zależności: wstrzykiwanie częściowo zainicjowanych obiektów

Mam dwie klasy z okrągłymi zależności:

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

class SecondClass 
{ 
    public readonly FirstClass First; 

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

Technicznie jest to możliwe do wystąpienia i prawidłowo wstrzykiwać zależności zarówno od nich, jeśli traktować je jako singletons:

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

Kiedy próbuję zrobić to samo z Unity, dostaję 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! 

Rozumiem, że Jedność próbuje chronić mnie przed używaniem częściowo zainicjowanych obiektów, ale chcę mieć tę ochronę jako opcję, a nie obowiązek.

Pytanie: czy obecne zachowanie jest nie do zniesienia?

Odpowiedz

5

Myślę, że nie można w ogóle używać okrągłych zależności z jednością.

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

+0

Nie lubię tej jedności nie może instancję klasy, ale mogę to zrobić ręcznie. Od dowiązania MDSN, które podałeś, żadna z następujących rzeczy nie jest prawdziwa dla mojego przypadku: -Obiekty generowane przez wstrzyknięcie konstruktora, które odwołują się do siebie w swoich parametrach konstruktora -Obiekty generowane przez zastrzyk konstruktora, gdy wystąpienie klasy jest przekazywane jako parametr do własnego konstruktora -Obiekty generowane przez wstrzyknięcie wywołania metody, które odwołują się do siebie -Obiekty generowane przez zastrzyk właściwości (setter), które odnoszą się do siebie –

+0

Problem polega na tym, że przez kilka chwil "FirstClass" żyje bez obiektu zależności, co oznacza, że ​​nie jest tak naprawdę ważny. Wszystko to w zasadzie zależy od tego, co robią twoje obiekty, kiedy twoja druga klasa spróbuje połączyć się z pierwszą, gdzie pierwsza oczekiwałaby, że obiekt zależny będzie dostępny, a następnie zawiedzie. Takie okrężne łańcuchy są problematyczne z kilku powodów, dlatego powinieneś unikać, jeśli to możliwe. –

+0

Wolałbym uzyskać NRE niż StackOverflowException, szczególnie biorąc pod uwagę, że mogę ręcznie tworzyć instancje moich obiektów. –

2

Jednym ze sposobów okrągłe to byłoby użyć leniwy załadunku na zależnościach na jednej z klas:

[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

To interesujący pomysł! Nie podoba mi się to, że moje klasy będą musiały wiedzieć o Unity i muszę używać Lazy zamiast prostych i wyraźnych odniesień. –

+0

Przepraszamy za te dwa lata później, ale ... Dokładnie tego szukam, ale to nie działa. Dostaję wyjątek rozwiązania zależności, mówiąc, że nie może rozwiązać "ILazy ". Czy powyższy kod nadal działa, czy też pojawiły się zmiany w Jedności, które wymagają innego podejścia? – Samo

+0

@Samo tak nadal działa dla mnie przeciwko Unity 2.Z jakiej wersji Unity korzystasz? Czy dokładnie skopiowałeś mój kod? Musisz zarejestrować otwarty ogólny kod ILazy, aby działał. (nb Napisałem to wcześniej .NET 4 przyszedł z własnym Lazy, który jest nieco inny) –

1

można użyć RegisterInstance zamiast RegisterType, aby osiągnąć swój cel. Będzie zachowywał się tak samo jak singleton - użyje tej samej instancji za każdym razem, gdy zostanie wywołany Resolve. Spójrz na ten przykład:

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

Wiwaty,

Pavel

+1

Ręcznie tworzysz oba obiekty, czy moje pytanie jest tym, co powiedziałem, że nie chcę tego robić, a moim głównym celem jest, aby Unity zrobiła to za mnie. stanie się znacznie brzydsza w aplikacjach świata rzeczywistego, gdy będziesz miał dziesiątki komponentów, nie tylko dwa. Również w próbce * pierwszy. Odtąd * i * drugi.Nazwy * nie będą takie same! –