C# 当需要相同类型的多个实例时,使用Unity进行DI

C# 当需要相同类型的多个实例时,使用Unity进行DI,c#,dependency-injection,unity-container,C#,Dependency Injection,Unity Container,我需要帮助。我使用Unity作为容器,并希望将同一类型的两个不同实例注入构造函数 class Example { Example(IQueue receiveQueue, IQueue sendQueue) {} } ..IQueue是在我的MessageQueue类中实现的 class MessageQueue : IQueue { MessageQueue(string path) {} } 如何将MessageQueue的两个不同实例注入示例类?要使用不同路径创建的每个M

我需要帮助。我使用Unity作为容器,并希望将同一类型的两个不同实例注入构造函数

class Example
{
   Example(IQueue receiveQueue, IQueue sendQueue) {}
}
..IQueue是在我的MessageQueue类中实现的

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

如何将MessageQueue的两个不同实例注入示例类?要使用不同路径创建的每个MessageQueue实例。

您可以使用名称注册这两个实例:

myContainer.RegisterInstance<IQueue>("ReceiveQueue", myReceiveMessageQueue);
myContainer.RegisterInstance<IQueue>("SendQueue", mySendMessageQueue);
或者注入unity容器,然后解析构造函数中的实例:

class Example
{
    Example(IUnityContainter container) 
    {
        _receiveQueue = container.Resolve<IQueue>("ReceiveQueue");
        _sendQueue = container.Resolve<IQueue>("SendQueue");
    }
}
类示例
{
示例(iUnityContainer容器)
{
_receiveQueue=container.Resolve(“receiveQueue”);
_sendQueue=container.Resolve(“sendQueue”);
}
}

我想这在Stackoverflow上已经被问过了。 您需要使用ParameterOverride:

ParameterOverride允许您传入构造函数参数的值,以重写传递给给定命名构造函数的参数。仅重写参数值,而不重写构造函数

var exampleInstance=new Example();
var queue1=unityContainer.Resolve(新参数重写{{“path”,“yourPath”});
var queue2=unityContainer.Resolve(新参数重写{{“path”,“yourPath2Queue2”});
exampleInstance.Example(队列1、队列2);
好吧,别这样

在这种情况下,您应该使用factory模式

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

它使意图更加明确,如果没有找到队列或队列配置不正确,您可以在
示例
类句柄中进行内部处理

并非所有内容都必须由容器自动连接。您可以这样注册
示例
类:

container.Register(新注入工厂(c=>
{
var receive=newmessagequeue(“receivePath”);
var send=newmessagequeue(“sendPath”);
返回新示例(接收、发送);
});

有很多方法可以实现您想要的结果(如多个答案所示)。下面是另一种使用命名注册(无属性)的方法:

IUnityContainer container=newunitycontainer();
container.RegisterType(“ReceiveQueue”,
新注入构造函数(“receivePath”);
container.RegisterType(“SendQueue”,
新的注入构造函数(“sendPath”);
container.RegisterType(
新注入构造函数(
新的ResolvedParameter(“ReceiveQueue”),
新的ResolvedParameter(“SendQueue”);
示例=container.Resolve();
这种方法的缺点是,如果更改了示例构造函数,则必须修改注册码以匹配。此外,该错误将是运行时错误,而不是更可取的编译时错误

您可以将上述内容与InjectionFactory结合起来,手动调用构造函数以进行编译时检查:

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>();
IUnityContainer container=newunitycontainer();
container.RegisterType(“ReceiveQueue”,
新注入构造函数(“receivePath”);
container.RegisterType(“SendQueue”,
新的注入构造函数(“sendPath”);
container.RegisterType(新注入工厂(c=>
新示例(c.Resolve(“接收队列”),
c、 解析(“发送队列”);
示例=container.Resolve();

如果您使用的是合成根,那么神奇字符串(“ReceiveQueue”和“SendQueue”)的使用将仅限于一个注册位置。

5年后,但我也在寻找这个答案。 我用自己的代码完成了它,然后决定使用OP提供的类(稍加修改)创建工作代码

这是一个完整的工作示例,您可以复制到其中并运行它

使用语句/统一库

您需要添加对Microsoft.Practices.Unity.dll的引用 您还需要添加一个using语句:

Microsoft.Practices.Unity
在LinqPad中,按F4添加引用和using语句(名称空间导入)


谢谢!但我想解析这样的示例,container.resolve()。我如何指定两个不同的MessageQueue实例?谢谢!这是我的第一个想法,但是我如何避免在工厂中更新MessageQueue类。我应该将容器注入工厂并使用另一个答案中建议的参数重写吗?工厂对于延迟创建实例特别有用,但我在您的示例中,您仍然在对象图构建过程中创建这些队列。我们甚至可以说您使
示例
的设计复杂化,因为它现在依赖于额外的抽象。我将从
示例
中删除
iquiefactory
依赖项,如果需要使用工厂,请使用n
InjectionFactory
如下:
container.Register(新的InjectionFactory(c=>newexample)(c.Resolve().Create(“Receive”)CResolve@ErikZ:好的。那么你是在不同的类之间共享队列?@jgauffin:不,队列只在示例类内部使用。如果使用工厂,队列将获得与示例类相同的生存期。因此,我认为这是最简单的解决方案,因为实际创建只会成为一个实现细节虽然这回答了这个问题,但这不是一个很好的解决方案,因为这会使应用程序与属性混淆,并将应用程序与Unity容器耦合。我宁愿使用
InjectionFactory
。很抱歉,但是您的第二个(已更新)示例是个坏主意,这就是为什么我投了反对票。你不应该提倡将容器注入类中,也就是说。@Steven你说得对,我收回了那篇编辑。链接的文章有一些非常有效的观点。OP只有一个类
MessageQueue
——没有单独的
ReceiveQueue
SendQueue
类。@Ere谢谢你提醒我这一点;我错过了这一点。这使我的答案更短。我更新了我的答案。@Ste
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>();
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>();
Microsoft.Practices.Unity
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{

}
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