6

Câu hỏi này là về Unity Container nhưng tôi đoán nó có thể áp dụng cho bất kỳ container phụ thuộc nào.Tiêm phụ thuộc: tiêm các đối tượng được khởi tạo một phần

Tôi có hai lớp với phụ thuộc vòng tròn:

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

class SecondClass 
{ 
    public readonly FirstClass First; 

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

Về mặt kỹ thuật nó có thể nhanh chóng và chính xác tiêm phụ thuộc cho cả hai nếu đối xử với họ như độc thân:

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

Khi tôi cố gắng làm tương tự với Unity, tôi nhận được 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! 

Tôi hiểu rằng Unity cố gắng bảo vệ tôi khỏi việc sử dụng các đối tượng được khởi tạo một phần nhưng tôi muốn có sự bảo vệ này như một lựa chọn, không phải là một nghĩa vụ.

Câu hỏi: hành vi hiện tại có thể không thể thực hiện được không?

Trả lời

5

Tôi nghĩ bạn không thể sử dụng phụ thuộc vòng tròn với sự đoàn kết chút nào.

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

+0

tôi không thích Unity rằng không thể nhanh chóng lớp nhưng tôi có thể làm điều đó bằng tay. Từ liên kết MDSN mà bạn đưa ra, không có trường hợp nào sau đây là đúng đối với trường hợp của tôi: -Bộ phận được tạo thông qua phép xây dựng tham chiếu lẫn nhau trong tham số hàm dựng của chúng -Objects được tạo thông qua hàm tạo constructor trong đó một thể hiện của một lớp được chuyển thành tham số cho hàm tạo riêng của nó -Khối từ được tạo ra thông qua phép gọi phương thức tham chiếu đến nhau -Bộ phận được tạo thông qua biến thuộc tính (setter) tham chiếu lẫn nhau –

+0

Vấn đề là trong một khoảnh khắc, "FirstClass" không có đối tượng phụ thuộc, có nghĩa là nó không thực sự hợp lệ. Tất cả điều này về cơ bản phụ thuộc vào những gì đối tượng của bạn làm khi xây dựng, nếu lớp thứ hai của bạn sẽ cố gắng gọi vào cái đầu tiên, nơi mà người thứ nhất mong đợi đối tượng phụ thuộc có sẵn, thì nó sẽ thất bại. Chuỗi thông tư như thế này là có vấn đề, vì nhiều lý do, vì vậy bạn nên cố gắng tránh, nếu có thể. –

+0

Tôi muốn nhận NRE hơn là StackOverflowException, đặc biệt là tính đến việc tôi có thể khởi tạo các đối tượng của mình theo cách thủ công. –

2

Một cách vòng này sẽ được sử dụng tải lười biếng cho sự phụ thuộc vào một trong các lớp:

[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

Đó là một ý tưởng thú vị! Những gì tôi không thích về nó là các lớp học của tôi sẽ phải biết về Unity và tôi phải sử dụng Lazy thay vì tài liệu tham khảo đơn giản và rõ ràng. –

+0

Xin lỗi để bước vào trong hai năm sau đó, nhưng ... Đây chính xác là những gì tôi đang tìm kiếm, nhưng nó không làm việc cho tôi. Tôi nhận được một ngoại lệ phân giải phụ thuộc, nói rằng nó không thể giải quyết 'ILazy '. Liệu mã trên vẫn hoạt động, hoặc đã có những thay đổi đối với Unity yêu cầu một cách tiếp cận khác? – Samo

+0

@Samo có vẫn làm việc cho tôi chống lại Unity 2.Bạn đang sử dụng phiên bản Unity nào? Bạn đã sao chép chính xác mã của tôi? Bạn cần đăng ký bản mở chung của ILazy để nó hoạt động. (nb tôi đã viết điều này trước khi .NET 4 đi kèm với kiểu Lazy riêng của nó hơi khác một chút) –

1

bạn có thể sử dụng RegisterInstance thay vì RegisterType để đạt được mục tiêu của bạn. Nó sẽ hoạt động giống như singleton - sẽ sử dụng cùng một cá thể mỗi khi Resolve được gọi. Hãy xem ví dụ sau:

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

Chúc mừng,

Pavel

+1

Bạn đang tạo thủ công cả hai đối tượng Câu hỏi của tôi là những gì tôi nói tôi không muốn làm Mục tiêu chính của tôi là Unity làm điều đó cho tôi. sẽ trở nên xấu hơn nhiều trong ứng dụng trong thế giới thực khi bạn sẽ có hàng tá thành phần, không chỉ hai thành phần. Ngoài ra trong mẫu của bạn * đầu tiên.Second * và * second.First * sẽ không giống nhau! –

Các vấn đề liên quan