简体   繁体   English

如何通过 ASP.NET Core 中的依赖注入获取对 IHostedService 的引用?

[英]How do I get a reference to an IHostedService via Dependency Injection in ASP.NET Core?

Details细节

I have attempted to create a background processing structure using the recommended IHostedService interface in ASP.NET 2.1.我尝试使用 ASP.NET 2.1 中推荐的IHostedService接口创建后台处理结构。 I register the services as follows:我按如下方式注册服务:

services.AddSingleton<AbstractProcessQueue<AbstractImportProcess>>();
services.AddHostedService<AbstractBackgroundProcessService<AbstractImportProcess>>();

services.AddSignalR();

The AbstractProcessQueue is just a wrapper around a BlockingCollection of processes that can be enqueued and dequeued. AbstractProcessQueue只是一个可以入队和出队的进程的BlockingCollection的包装器。 The AbstractBackgroundProcessService implements the IHostedService interface and looks at the queue for new processes it can start. AbstractBackgroundProcessService实现IHostedService接口并查看队列以查找它可以启动的新进程。

Now, the trouble starts when, inside a SignalR hub, I attempt to get a reference to the background processing service via the Dependency Injection mechanisms.现在,当我在SignalR中心内尝试通过Dependency Injection机制获取对后台处理服务的引用时,问题就开始了。 I have tried the following solutions, but none seem to be working as intended:我尝试了以下解决方案,但似乎都没有按预期工作:

Option 1:选项1:

public HubImportClient(IServiceProvider provider)
{
    //This returns null.
    var service = provider.GetService<AbstractBackgroundProcessService<AbstractImportProcess>>();
}

Option 2:选项 2:

public HubImportClient(IServiceProvider provider)
{
    //This returns null.
    var service = (AbstractBackgroundProcessService<AbstractImportProcess>) provider.GetService(typeof(AbstractBackgroundProcessService<AbstractImportProcess>>));
}

Option 3:选项 3:

public HubImportClient(IServiceProvider provider)
{
    //This throws an exception, because the service is missing.
    var service = provider.GetRequiredService<AbstractBackgroundProcessService<AbstractImportProcess>>();
}

Option 4:选项 4:

public HubImportClient(IServiceProvider provider)
{
    //This throws an exception, because the service is missing.
    var service = (AbstractBackgroundProcessService<AbstractImportProcess>) provider.GetRequiredService(typeof(AbstractBackgroundProcessService<AbstractImportProcess>);
}

Option 5:选项 5:

public HubImportClient(IServiceProvider provider)
{
    //This returns a correct service, but prevents me from adding additional AbstractBackgroundProcessService implementations with different type parameters.
    //Additionally, it seems like this reference was newly created, and not the instance that was created on application startup (i.e. the hash codes are different, and the constructor is called an additional time).
    var service = provider.GetService<IHostedService>();
    if(service is AbstractBackgroundProcessService<AbstractProcessService>)
    {    this.Service = (AbstractBackgroundProcessService<AbstractProcessService>) service;}
}

Option 6:选项 6:

public HubImportClient(IServiceProvider provider)
{
    //This works similarly to the previous option, and allows multiple implementations, but the constructor is still called twice and the instances thus differ.
    AbstractBackgroundProcessService<AbstractImportProcess> service = null;
    foreach(IHostedService service in provider.GetServices<IHostedService>())
    {
        if(service is AbstractBackgroundProcessService<AbstractImportProcess>)
        {
            service = (AbstractBackgroundProcessService<AbstractImportProcess>) service;
            break;
        }
    }  
}

Option 7:选项 7:

public HubImportClient(IServiceProvider provider)
{
    //This just skips the for each loop all together, because no such services could be found.
    AbstractBackgroundProcessService<AbstractImportProcess> service = null;
    foreach(AbstractBackgroundProcessService<AbstractImportProcess> current in provider.GetServices<AbstractBackgroundProcessService<AbstractImportProcess> >())
    {
        service = current;
        break;
    }    
}

Option 8:选项 8:

//This works, but prevents multiple implementations again.
public HubImportClient(IHostedService service)
{
    this.Service = service;   
}

Option 9:选项 9:

//This does not work again.
public HubImportClient(AbstractBackgroundProcessService<AbstractImportProcess> service)
{
    this.Service = service;   
}

Question

So then my question remains: how am I supposed to get a reference to an IHostedService implementation so that:那么我的问题仍然存在:我应该如何获得对IHostedService实现的引用,以便:

(a): I can inject multiple instances of the service that differ only by their type parameter (eg a hosted service for AbstractImportProcess es as well as one for AbstractExportProcess es) (a):我可以注入仅类型参数不同的服务的多个实例(例如,一个用于AbstractImportProcess es 的托管服务以及一个用于AbstractExportProcess es 的托管服务)

(b): there is only ever one instance of the IHostedService for that specific type parameter. (b):对于该特定类型参数,只有一个IHostedService实例。

Thanks in advance for any help!在此先感谢您的帮助!

Current workaround from mentioned git page :提到的git page中的当前解决方法:

services.AddSingleton<YourServiceType>();
services.AddSingleton<IHostedService>(p => p.GetRequiredService<YourServiceType>());

Or, if your service implements some other interfaces:或者,如果您的服务实现了一些其他接口:

services.AddSingleton<YourServiceType>();    
services.AddSingleton<IYourServiceType>(p => p.GetRequiredService<YourServiceType>());
services.AddSingleton<IHostedService>(p => p.GetRequiredService<YourServiceType>());

This creates your service as hosted (runs and stops at host's start and shutdown), as well as gets injected as depedency wherever you require it to be.这会将您的服务创建为托管服务(在主机启动和关闭时运行和停止),并在您需要的任何地方作为依赖注入。

Update:更新:

I don't see this solution as a "workaround" anymore.我不再将此解决方案视为“解决方法”。

Instead, I would describe it this way: hosted component and a regular service are entities of different types, each one serving its own purpose.相反,我会这样描述它:托管组件和常规服务是不同类型的实体,每个实体都有自己的用途。

The solution above, however, allows one to combine them, registering a hosted component as a service, which can be used in the dependency resolution chain.然而,上面的解决方案允许将它们组合起来,将托管组件注册为服务,可以在依赖关系解析链中使用。

Which is awesome.这太棒了。

This is just a slight modification to the answer by @AgentFire.这只是@AgentFire 对答案的轻微修改。 This method is clearer and allows for several background hosted services in a single Web Service.此方法更清晰,并允许在单个 Web 服务中使用多个后台托管服务。

services.AddSingleton<YourServiceType>();
services.AddHostedService<YourServiceType>(p => p.GetRequiredService<YourServiceType>());

There has been some discussion around this topic.围绕这个话题进行了一些讨论。 For example, see: https://github.com/aspnet/Hosting/issues/1489 .例如,请参阅: https ://github.com/aspnet/Hosting/issues/1489。 One of the problems that you'll run into is that hosted services are added as transient services (from ASP.NET Core 2.1+), meaning that resolving an hosted service from the dependency injection container will result in a new instance each time.您将遇到的问题之一是托管服务作为瞬态服务添加(来自 ASP.NET Core 2.1+),这意味着从依赖项注入容器解析托管服务每次都会产生一个新实例。

The general advice is to encapsulate any business logic that you want to share with or interact from other services into a specific service.一般建议是将您想要与其他服务共享或与其他服务交互的任何业务逻辑封装到特定服务中。 Looking at your code I suggest you implement the business logic in the AbstractProcessQueue<AbstractImportProcess> class and make executing the business logic the only concern of AbstractBackgroundProcessService<T> .查看您的代码,我建议您在AbstractProcessQueue<AbstractImportProcess>类中实现业务逻辑,并使执行业务逻辑成为AbstractBackgroundProcessService<T>的唯一关注点。

In.Net Core 3.1 and.Net 5.0you can get references to the existing instances of the Hosted Services with:在 .Net Core 3.1 和 .Net 5.0 中,您可以通过以下方式获取对托管服务的现有实例的引用:
IEnumerable<IHostedService> allHostedServices = this.serviceProvider.GetService<IEnumerable<IHostedService>>();

You get this directly from the IServiceProvider in dotnet core 3.1 and later:您可以直接从 dotnet core 3.1 及更高版本中的IServiceProvider获得此信息:

var myHostedService = serviceProvider
    .GetServices<IHostedService>()
    .OfType<MyHostedService>()
    .Single();

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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