简体   繁体   中英

DI with Unity when multiple instances of the same type is needed

I need help with this. I'm using Unity as my container and I want to inject two different instances of the same type into my constructor.

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

....and IQueue is implemented in my MessageQueue class....

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

How can I inject two different instances of MessageQueue into my Example class? Each of the MessageQueue instances to be created with different path.

There are many ways to achieve the results you want (as evidenced by the multiple answers). Here is another way using named registrations (without attributes):

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

The downside of this approach is that if the Example constructor is changed then the registration code must also be modified to match. Also, the error would be a runtime error and not a more preferable compile time error.

You could combine the above with an InjectionFactory to invoke the constructor manually to give compile time checking:

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

If you are using a composition root then the use of the magic strings ("ReceiveQueue" and "SendQueue") would be limited to the one registration location.

You could register the two instances with names:

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

and then you should be able to resolve by name, but it requires using the Dependency attribute:

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

or inject the unity container and then resolve the instances within the constructor:

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

Not everything has to be automatically wired by the container. You can register the Example class like this:

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

Well, don't

You should use the factory pattern in this case.

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

It makes the intention a lot more clear and you can internally in the Example class handle if the queues are not found or incorrectly configured.

I think this has been asked before on Stackoverflow. You need to use ParameterOverride:

ParameterOverride enables you to pass in values for constructor parameters to override a parameter passed to a given named constructor. Only the parameter value is overridden, not the constructor.

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

5 years later, but I was looking for this answer too. I worked through it with my own code and then decided to create working code using (slightly altered) classes the OP provided.

This is an entire working example that you can copy into LINQPad (programmer's playground) and run.

Using Statement / Unity Libary

You'll need to add a reference to Microsoft.Practices.Unity.dll You'll also need to add a using statement of :

Microsoft.Practices.Unity

In LinqPad you press F4 to add the reference and the using statement (namespace import).

void Main()
{
    // Create your unity container (one-time creation)
    UnityContainer uc = new UnityContainer();

    // Create simple list to hold your target objects
    // (makes the sample easy to follow)
    List<MessageQueue> allMQs = new List<MessageQueue>();

    // I'm adding TransientLifetimeManager() in order to 
    // explicitly ask for new object creation each time
    // uc.Resolve<MessageQueue>() is called
    uc.RegisterType<IQueue, MessageQueue>(new TransientLifetimeManager());
// ### override the parameters by matching the parameter name (inPath)
    var item = uc.Resolve<MessageQueue>(new ParameterOverride("inPath", "extra.txt").OnType<MessageQueue>());
    allMQs.Add(item);
    item = uc.Resolve<MessageQueue>(new ParameterOverride("inPath", "super.txt").OnType<MessageQueue>());
    allMQs.Add(item);

    foreach (MessageQueue mq in allMQs){
        Console.WriteLine($"mq.Path : {mq.Path}");
    }

    Console.WriteLine("######################\n");

    uc.RegisterType<Example>(new InjectionConstructor((allMQs[0] as IQueue),(allMQs[1] as IQueue)));

    // #### Create a new Example from the UnityContainer
    var example1 = uc.Resolve<Example>();
    // ##### Notice that the Example object uses the default values of super.txt & extra.txt

    Console.WriteLine("#### example1 obj. uses default values ###########");
    Console.WriteLine($"example1.receiver.Path : {example1.receiver.Path}");
    Console.WriteLine($"example1.sender.Path : {example1.sender.Path}");

    // ##################################################
    // Override the parameters that he Example class uses.
 // ### override the parameters by matching the parameter 
 // names (receiveQueue, sendQueue) found in the target
// class constructor (Example class)
    var example2 = uc.Resolve<Example>( 
        new ParameterOverrides {
             {"receiveQueue", new MessageQueue("newReceiveFile")},
             { "sendQueue", new MessageQueue("newSendFile")}
        }.OnType<Example>());

    Console.WriteLine("######################\n");

    Console.WriteLine("#### example1 obj. uses ParameterOverride values ###########");
    Console.WriteLine($"example2.sender.Path : {example2.sender.Path}");
    Console.WriteLine($"example2.receiver.Path : {example2.receiver.Path}");
}

class Example
{
   public MessageQueue receiver {get;set;}
   public MessageQueue sender {get;set;}

   public Example(IQueue receiveQueue, IQueue sendQueue) {
    this.receiver = receiveQueue as MessageQueue;
    this.sender = sendQueue as MessageQueue;

   }
}

public class MessageQueue : IQueue
{
    public string Path {get;set;}
    public MessageQueue(string inPath) {
        Path = inPath;}
}

interface IQueue{

}

Output For Examination

If you run the script above you'll see sample output which will look like the following:

mq.Path : extra.txt
mq.Path : super.txt
######################

#### example1 obj. uses default values ###########
example1.receiver.Path : extra.txt
example1.sender.Path : super.txt
######################

#### example1 obj. uses ParameterOverride values ###########
example2.sender.Path : newSendFile
example2.receiver.Path : newReceiveFile

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM