我们在.net核心中有一个基于服务的应用程序,它将在Linux环境中作为守护程序运行。一切都按预期工作,但我在处理依赖项注入时遇到问题。 下面是供参考的代码
Program.cs
class Program
{
static async Task Main(string[] args)
{
Console.WriteLine("Starting PreProcessor Application ");
try
{
ConfigParameters.LoadSettings(args);
}
catch (Exception ex)
{
Console.BackgroundColor = ConsoleColor.Red;
Console.WriteLine($"Error in setting config parameters {ex.Message}");
return;
}
IHost host = new HostBuilder()
.ConfigureServices((hostContext, services) =>
{
services.AddLogging();
services.AddHostedService<MainService>();
services.AddTransient<IMessageQueue, ActiveMQHandler>(x =>
{
return new ActiveMQHandler(ConfigParameters.Settings.MessageQueueAddress);
});
services.AddTransient<IMessageQueue, ActiveMQHandler>(x =>
{
return new ActiveMQHandler(ConfigParameters.Settings.MessageQueueAddress);
});
services.AddTransient<IMessageQueue, ActiveMQHandler>(x =>
{
return new ActiveMQHandler(ConfigParameters.Settings.MessageQueueAddress);
});
})
.Build();
await host.RunAsync();
}
}
MainService
的构造函数如下
IApplicationLifetime appLifetime;
IConfiguration configuration;
PreProcessorQueueListener listener;
private string reason = "SHUTDOWN SIGNAL";
private IMessageQueue messageQueue;
private IMessageQueue messageQueueSL;
private IMessageQueue messageQueueSLProcess;
public MainService(IConfiguration configuration, IApplicationLifetime appLifetime, IMessageQueue messageQueue, IMessageQueue messageQueueSL, IMessageQueue messageQueueSLProcess)
{
this.configuration = configuration;
this.messageQueue = messageQueue;
this.messageQueueSL = messageQueueSL;
this.messageQueueSLProcess = messageQueueSLProcess;
this.appLifetime = appLifetime;
}
如果您在我的MainService
代码中看到,我正在使用构造函数依赖注入为IMessageQueue
接口传递三个实例。我真正想要的是基于应用程序任何部分的需求,我可以通过传递ActiveMQHandler
接口来获取IMessageQueue
类的新实例。由于我无法为此找到解决方案,因此我传递了三个IMessageQueue
实例(对此解决方案我不满意)。如果我需要使用ActiveMQHandler
类的另一个实例,则必须在我的IMessageQueue
类中将第四个参数作为MainService
接口传递。
我真正要寻找的是使用ServiceProvider
(或更优雅的东西),并使用它来获取类的实例的新实例(基于Program.cs
的定义)实现IMessageQueue
接口。
一个建议的家伙?
答案 0 :(得分:2)
如果将MainService构造函数签名更改为
public MainService(IConfiguration configuration, IApplicationLifetime appLifetime, IEnumerable<IMessageQueue> messageQueues)
您将能够访问所有三个接口实现。
问题可能出在您是否需要从列表中识别它们,例如对每个实现执行不同的操作。如果您需要在每个实现上执行相同的操作,那么它将起作用。
否则,您应该考虑使用泛型类型来区分注入的实现。
答案 1 :(得分:1)
只需将构造函数更改为包含IEnumerable<IMessageQueue>
。
它应该为您提供所有已注册的IMessageQueue实现者的列表。
我个人不喜欢在类中依赖IApplicationLifetime或IServiceProvider。这有点像ServiceLocator的反模式。
答案 2 :(得分:0)
您可以将IServiceProvider
注入到您的类中,然后使用GetServices(typeof(IMessageQueue))
命名空间中的GetServices<IMessageQueue>()
或扩展功能Microsoft.Extensions.DependencyInejction
。像这样:
public MainService(IConfiguration configuration, IApplicationLifetime appLifetime, IServiceProvider serviceProvider)
{
this.configuration = configuration;
messageQueue = serviceProvider.GetServices<IMessageQueue>();
messageQueueSL = serviceProvider.GetServices<IMessageQueue>();
messageQueueSLProcess = serviceProvider.GetServices<IMessageQueue>();
this.appLifetime = appLifetime;
}
根据您使用IMessageQueue
的确切用途,可能会有更优雅的解决方案。 IMessageQueue
似乎用于某种日志记录。例如,假设您需要为每个类别的消息队列,其中SLProcess
和SL
是不同的类别。对于这种情况,您可以注入泛型。因此,您可以定义以下内容:
interface IMessageQueue<T> : IMessageQueue { }
class ActiveMQHandler<T> : ActiveMQHandler, IMessageQueue<T> {
public string targetType => typeof(T).ToString();
}
使用此方法,您应该可以注入以下内容:AddTransient(typeof(IMessageQueue<>), typeof(ActiveMQHandler<>))
。
答案 3 :(得分:0)
最后,我提供了一个我认为是优雅的解决方案,并且不依赖于构造函数DI。
想法是让服务(是的,我们有一个微服务体系结构)在IServiceCollection
中创建依赖关系的集合,一旦服务启动,只要任何类想要解析依赖关系,它们都将传入Interface
并获取具体类的实例。
我的最终代码是这样的。我在公共库中创建了一个单独的类
public class DependencyInjection
{
private static ServiceProvider Provider;
public static void AddServices(IServiceCollection services)
{
Provider = services.BuildServiceProvider();
}
public static T GetService<T>()
{
var serviceScopeFactory = Provider.GetRequiredService<IServiceScopeFactory>();
using (var scope = serviceScopeFactory.CreateScope())
{
return scope.ServiceProvider.GetService<T>();
}
}
}
现在,我在Main
文件中的Program.cs
方法看起来像这样
static async Task Main(string[] args)
{
Console.WriteLine("Starting PreProcessor Application ");
IServiceCollection servicesCollection = new ServiceCollection();
try
{
ConfigParameters.LoadSettings(args);
servicesCollection.AddScoped<IMessageQueue, ActiveMQHandler>(x =>
{
return new ActiveMQHandler("127.0.0.1");
});
DependencyInjection.AddServices(servicesCollection);
}
catch (Exception ex)
{
Console.BackgroundColor = ConsoleColor.Red;
Console.WriteLine($"Error in setting config parameters {ex.Message}");
return;
}
IHost host = new HostBuilder()
.ConfigureHostConfiguration(configHost =>
{
configHost.AddCommandLine(args);
})
.ConfigureServices((hostContext, services) =>
{
services.AddLogging();
services.AddHostedService<MainService>();
})
.Build();
await host.RunAsync();
}
现在在项目中的任何地方,当我需要ActiveMQHandler类的实例时,我只需编写下面的代码行
var messageQueue = DependencyInjection.GetService<IMessageQueue>();
仅出于我Program.cs
中的信息,我正在使用AddScoped
,但是我也已经使用AddSingleton
测试了代码,每次我请求具体的类实例时,它都是相同的。
此链接https://stackify.com/net-core-dependency-injection/上的文章对我有帮助