问题
Details
I have attempted to create a background processing structure using the recommended IHostedService
interface in ASP.NET 2.1. 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. The AbstractBackgroundProcessService
implements the IHostedService
interface and looks at the queue for new processes it can start.
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. I have tried the following solutions, but none seem to be working as intended:
Option 1:
public HubImportClient(IServiceProvider provider)
{
//This returns null.
var service = provider.GetService<AbstractBackgroundProcessService<AbstractImportProcess>>();
}
Option 2:
public HubImportClient(IServiceProvider provider)
{
//This returns null.
var service = (AbstractBackgroundProcessService<AbstractImportProcess>) provider.GetService(typeof(AbstractBackgroundProcessService<AbstractImportProcess>>));
}
Option 3:
public HubImportClient(IServiceProvider provider)
{
//This throws an exception, because the service is missing.
var service = provider.GetRequiredService<AbstractBackgroundProcessService<AbstractImportProcess>>();
}
Option 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:
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:
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:
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:
//This works, but prevents multiple implementations again.
public HubImportClient(IHostedService service)
{
this.Service = service;
}
Option 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:
(a): I can inject multiple instances of the service that differ only by their type parameter (e.g. a hosted service for AbstractImportProcess
es as well as one for AbstractExportProcess
es)
(b): there is only ever one instance of the IHostedService
for that specific type parameter.
Thanks in advance for any help!
回答1:
There has been some discussion around this topic. For example, see: 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.
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>
.
回答2:
Current workaround from mentioned git page:
services.AddSingleton<YourServiceType>();
services.AddSingleton<IHostedService>(p => p.GetService<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 required it.
来源:https://stackoverflow.com/questions/52036998/how-do-i-get-a-reference-to-an-ihostedservice-via-dependency-injection-in-asp-ne