web-dev-qa-db-fra.com

Comment obtenir une référence à un IHostedService via l'injection de dépendances dans ASP.NET Core?

Détails

J'ai essayé de créer une structure de traitement en arrière-plan à l'aide de l'interface IHostedService recommandée dans ASP.NET 2.1. J'enregistre les services comme suit:

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

services.AddSignalR();

AbstractProcessQueue n'est qu'un wrapper autour d'un BlockingCollection de processus qui peuvent être mis en file d'attente et retirés de la file d'attente. AbstractBackgroundProcessService implémente l'interface IHostedService et recherche dans la file d'attente les nouveaux processus qu'elle peut démarrer.

Maintenant, le problème commence lorsque, à l'intérieur d'un hub SignalR, j'essaie d'obtenir une référence au service de traitement en arrière-plan via le Dependency Injection mécanismes. J'ai essayé les solutions suivantes, mais aucune ne semble fonctionner comme prévu:

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

Alors ma question demeure: comment suis-je censé obtenir une référence à une implémentation IHostedService pour que:

(a): Je peux injecter plusieurs instances du service qui ne diffèrent que par leur paramètre de type (par exemple, un service hébergé pour AbstractImportProcesses ainsi qu'un pour AbstractExportProcesses)

(b): il n'y a qu'une seule instance de IHostedService pour ce paramètre de type spécifique.

Merci d'avance pour votre aide!

12
Teun Kooijman

Il y a eu quelques discussions sur ce sujet. Par exemple, voir: https://github.com/aspnet/Hosting/issues/1489 . L'un des problèmes que vous rencontrerez est que les services hébergés sont ajoutés en tant que services transitoires (à partir d'ASP.NET Core 2.1+), ce qui signifie que la résolution d'un service hébergé à partir du conteneur d'injection de dépendance se traduira à chaque fois par une nouvelle instance.

Le conseil général consiste à encapsuler toute logique métier que vous souhaitez partager ou interagir avec d'autres services dans un service spécifique. En regardant votre code, je vous suggère d'implémenter la logique métier dans le AbstractProcessQueue<AbstractImportProcess> classe et faire de l'exécution de la logique métier la seule préoccupation de AbstractBackgroundProcessService<T>.

6
Henk Mollema