当需要多个相同类型的实例时,使用Unity进行DI
本文关键字:使用 Unity DI 进行 实例 同类型 | 更新日期: 2023-09-27 18:29:11
我需要帮助。我使用Unity作为我的容器,我想将同一类型的两个不同实例注入到我的构造函数中。
class Example
{
Example(IQueue receiveQueue, IQueue sendQueue) {}
}
并且IQueue是在我的MessageQueue类中实现的。。。。
class MessageQueue : IQueue
{
MessageQueue(string path) {}
}
如何将MessageQueue的两个不同实例注入到Example类中?要使用不同路径创建的每个MessageQueue实例。
有很多方法可以实现您想要的结果(多个答案证明了这一点)。以下是使用命名注册(无属性)的另一种方法:
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>();
这种方法的缺点是,如果更改了Example构造函数,则还必须修改注册代码以匹配。此外,该错误将是运行时错误,而不是更可取的编译时错误。
您可以将以上内容与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>();
如果您使用的是组合根,那么魔术字符串("ReceiveQueue"answers"SendQueue")的使用将仅限于一个注册位置。
并非所有内容都必须由容器自动连接。您可以这样注册Example
类:
container.Register<Example>(new InjectionFactory(c =>
{
var receive = new MessageQueue("receivePath");
var send = new MessageQueue("sendPath");
return new Example(receive, send);
});
您可以用名称注册这两个实例:
myContainer.RegisterInstance<IQueue>("ReceiveQueue", myReceiveMessageQueue);
myContainer.RegisterInstance<IQueue>("SendQueue", mySendMessageQueue);
然后您应该能够按名称解析,但它需要使用Dependency
属性:
class Example
{
Example([Dependency("ReceiveQueue")] IQueue receiveQueue,
[Dependency("SendQueue")] IQueue sendQueue) {
}
}
或注入统一容器,然后在构造函数中解析实例:
class Example
{
Example(IUnityContainter container)
{
_receiveQueue = container.Resolve<IQueue>("ReceiveQueue");
_sendQueue = container.Resolve<IQueue>("SendQueue");
}
}
好吧,不要
在这种情况下,您应该使用工厂模式。
class Example
{
Example(IQueueFactory factory)
{
_sendQueue = factory.Create("MySend");
_receiveQueue = factory.Create("MyReceive");
}
}
它使意图更加明确,并且如果没有找到队列或队列配置不正确,您可以在Example
类内部进行处理。
5年后,但我也在寻找这个答案。我用自己的代码完成了它,然后决定使用OP提供的(稍微修改过的)类来创建工作代码。
这是一个完整的工作示例,您可以将其复制到LINQPad(程序员的游乐场)中并运行。
使用Statement/Unity Libary
您需要添加对Microsoft.Practices.Unity.dll的引用您还需要添加一个使用声明:
Microsoft.Practices.Unity
在LinqPad中,按F4添加引用和using语句(命名空间导入)。
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
我想以前在Stackoverflow上有人问过这个问题。您需要使用ParameterOverride:
ParameterOverride使您能够传入构造函数参数的值,以覆盖传递给给定命名构造函数的参数。只有参数值被重写,而不是构造函数。
MSDN文章链接
链接到堆叠溢出文章
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);