2013-08-27 35 views
9

Tôi cần trợ giúp với điều này. Tôi đang sử dụng Unity làm vùng chứa của mình và tôi muốn tiêm hai cá thể khác nhau cùng loại vào hàm tạo của tôi.DI với Unity khi cần nhiều phiên bản cùng loại

class Example 
{ 
    Example(IQueue receiveQueue, IQueue sendQueue) {} 
} 

.... và IQueue được thực hiện trong lớp MessageQueue của tôi ....

class MessageQueue : IQueue 
{ 
    MessageQueue(string path) {} 
} 

Làm thế nào tôi có thể tiêm hai trường hợp khác nhau của MessageQueue vào lớp Ví dụ của tôi? Mỗi cá thể MessageQueue được tạo với đường dẫn khác nhau.

Trả lời

3

Bạn có thể đăng ký hai trường hợp với những cái tên:

myContainer.RegisterInstance<IQueue>("ReceiveQueue", myReceiveMessageQueue); 
myContainer.RegisterInstance<IQueue>("SendQueue", mySendMessageQueue); 

và sau đó bạn sẽ có thể giải quyết bằng tên, nhưng nó đòi hỏi sử dụng Dependency thuộc tính:

class Example 
{ 
    Example([Dependency("ReceiveQueue")] IQueue receiveQueue, 
      [Dependency("SendQueue")] IQueue sendQueue) { 
    } 
} 

hoặc tiêm thùng chứa thống nhất và sau đó giải quyết các phiên bản bên trong hàm tạo:

class Example 
{ 
    Example(IUnityContainter container) 
    { 
     _receiveQueue = container.Resolve<IQueue>("ReceiveQueue"); 
     _sendQueue = container.Resolve<IQueue>("SendQueue"); 
    } 
} 

+1

Mặc dù điều này trả lời câu hỏi, nó không phải là một giải pháp rất tốt, bởi vì điều này clutters các ứng dụng với các thuộc tính và các cặp vợ chồng ứng dụng vào container Unity. Thay vào đó, tôi muốn sử dụng 'InjectionFactory'. – Steven

+1

Tôi xin lỗi, nhưng ví dụ thứ hai (cập nhật) của bạn là một ý tưởng tồi, đó là lý do tại sao bạn nhận được yêu cầu của tôi. Bạn không nên quảng cáo tiêm vào các lớp học, đó là [thực hành xấu] (http://blog.ploeh.dk/2010/02/03/ServiceLocatorisanAnti-Pattern/). – Steven

+1

@Steven bạn nói đúng, tôi lấy lại bản chỉnh sửa đó. bài viết được liên kết có một số điểm rất hợp lệ. –

1

Tôi nghĩ điều này đã được hỏi trước trên Stackoverflow. Bạn cần phải sử dụng ParameterOverride:

Thông sốOverride cho phép bạn chuyển giá trị cho tham số hàm tạo để ghi đè tham số được truyền cho một hàm dựng có tên. Chỉ giá trị tham số được ghi đè, chứ không phải hàm tạo.

Link to MSDN Article

Link to Stackoverflow Article

var exampleInstance = new Example(); 

var queue1 = unityContainer.Resolve<IQueue>(new ParameterOverrides<MessageQueue> { { "path", "yourPath" }}); 

var queue2 = unityContainer.Resolve<IQueue>(new ParameterOverrides<MessageQueue> { { "path", "yourPath2Queue2" }}); 

exampleInstance.Example(queue1,queue2); 
+0

Cảm ơn bạn! Nhưng tôi muốn giải quyết Ví dụ như thế này, container.Resolve (). Làm cách nào để chỉ định hai phiên bản MessageQueue khác nhau? –

2

Vâng, không

Bạn nên sử dụng các mô hình nhà máy trong trường hợp này.

class Example 
{ 
    Example(IQueueFactory factory) 
    { 
     _sendQueue = factory.Create("MySend"); 
     _receiveQueue = factory.Create("MyReceive"); 
    } 
} 

Điều này làm cho ý định rõ ràng hơn và bạn có thể nội bộ trong bảng điều khiển lớp Example nếu không tìm thấy hàng đợi hoặc cấu hình không chính xác.

+0

Cảm ơn bạn! Đó là suy nghĩ đầu tiên của tôi, nhưng làm thế nào để tránh làm mới các lớp MessageQueue trong nhà máy. Tôi có nên tiêm container vào nhà máy và sử dụng ParameterOverrides được đề xuất trong câu trả lời khác không? –

+0

Một nhà máy đặc biệt hữu ích cho việc trì hoãn việc tạo ra các cá thể, nhưng trong ví dụ của bạn, bạn vẫn đang tạo các hàng đợi đó trong khi xây dựng đồ thị đối tượng. Chúng tôi thậm chí có thể lập luận rằng bạn phức tạp thiết kế của 'Ví dụ', vì nó bây giờ phụ thuộc vào một trừu tượng thêm. Tôi sẽ loại bỏ phụ thuộc 'IQueueFactory' khỏi' Example' và nếu sử dụng nhà máy là cần thiết, hãy đăng ký nó bằng cách sử dụng một 'InjectionFactory' như sau:' container.Register (new InjectionFactory (c => new Example (c.Resolve () Tạo ("Nhận"), c.Rolveolve Steven

+0

@ErikZ: Ok. vì vậy bạn đang chia sẻ hàng đợi giữa các lớp khác nhau? – jgauffin

3

Không phải mọi thứ phải được hộp chứa tự động chứa. Bạn có thể đăng ký lớp Example như thế này:

container.Register<Example>(new InjectionFactory(c => 
{ 
    var receive = new MessageQueue("receivePath"); 
    var send = new MessageQueue("sendPath"); 
    return new Example(receive, send); 
}); 
+0

OP có một lớp 'MessageQueue' duy nhất - không có các lớp' ReceiveQueue' và 'SendQueue' riêng biệt. –

+0

@ ErenErsönmez: Cảm ơn vì đã chú ý đến điều này; Tôi đã bỏ lỡ điểm này. Điều này làm cho câu trả lời của tôi ngắn hơn nhiều. Tôi đã cập nhật câu trả lời của mình. – Steven

+0

@Steven: Câu trả lời ban đầu của bạn thực sự thú vị. Nếu việc tạo các lớp và giao diện mới là một lựa chọn, tôi nghĩ rằng giải pháp đó có vẻ tốt. –

7

Có rất nhiều cách để đạt được kết quả mong muốn (bằng chứng là nhiều câu trả lời). Dưới đây là một cách sử dụng tên đăng ký (không có thuộc tính):

IUnityContainer container = new UnityContainer(); 

container.RegisterType<IQueue, MessageQueue>("ReceiveQueue", 
    new InjectionConstructor("receivePath")); 

container.RegisterType<IQueue, MessageQueue>("SendQueue", 
    new InjectionConstructor("sendPath")); 

container.RegisterType<Example>(
    new InjectionConstructor(
     new ResolvedParameter<IQueue>("ReceiveQueue"), 
     new ResolvedParameter<IQueue>("SendQueue"))); 

Example example = container.Resolve<Example>(); 

Nhược điểm của phương pháp này là nếu các nhà xây dựng Ví dụ được thay đổi thì mã số đăng ký cũng phải được sửa đổi để phù hợp. Ngoài ra, lỗi sẽ là một lỗi thời gian chạy và không phải là một lỗi thời gian biên dịch thích hợp hơn.

Bạn có thể kết hợp trên với một InjectionFactory để gọi các nhà xây dựng bằng tay để cung cấp cho biên dịch kiểm tra thời gian:

IUnityContainer container = new UnityContainer(); 

container.RegisterType<IQueue, MessageQueue>("ReceiveQueue", 
    new InjectionConstructor("receivePath")); 

container.RegisterType<IQueue, MessageQueue>("SendQueue", 
    new InjectionConstructor("sendPath")); 

container.RegisterType<Example>(new InjectionFactory(c => 
    new Example(c.Resolve<IQueue>("ReceiveQueue"), 
       c.Resolve<IQueue>("SendQueue")))); 

Example example = container.Resolve<Example>(); 

Nếu bạn đang sử dụng một gốc thành phần sau đó việc sử dụng các dây ma thuật ("ReceiveQueue" và " SendQueue ") sẽ bị giới hạn ở một địa điểm đăng ký.

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