web-dev-qa-db-fra.com

Injection de dépendance de plusieurs instances du même type dans ASP.NET Core 2

Dans l’API Web Core 2 d’ASP.NET, je souhaite utiliser l’injection de dépendance pour injecter l’instance httpClientA de HttpClient à ControllerA, ainsi qu’une instance httpClientB de HttpClient à ControllerB.

Le code d'enregistrement de la DI ressemblerait à quelque chose comme:

HttpClient httpClientA = new HttpClient();
httpClientA.BaseAddress = endPointA;
services.AddSingleton<HttpClient>(httpClientA);

HttpClient httpClientB = new HttpClient();
httpClientB.BaseAddress = endPointB;
services.AddSingleton<HttpClient>(httpClientB);

Je sais que je pourrais sous-classe HttpClient pour créer un type unique pour chaque contrôleur, mais cela ne s'adapte pas très bien.

Quel est un meilleur moyen?

UPDATE En ce qui concerne spécifiquement HttpClient, Microsoft semble avoir quelque chose en projet 

https://github.com/aspnet/HttpClientFactory/blob/dev/samples/HttpClientFactorySample/Program.cs#L32 - Merci à @ mountain-traveller (Dylan) pour l'avoir signalé.

13
Bryan

Utiliser les inscriptions nommées

C'est exactement ce que les enregistrements nommés sont pour.

Inscrivez-vous comme ceci:

container.RegisterInstance<HttpClient>(new HttpClient(), "ClientA");
container.RegisterInstance<HttpClient>(new HttpClient(), "ClientB");

Et récupérer de cette façon:

var clientA = container.Resolve<HttpClient>("ClientA");
var clientB = container.Resolve<HttpClient>("ClientB");

Si vous souhaitez que ClientA ou ClientB soit automatiquement injecté dans un autre type enregistré, consultez cette question . Exemple:

container.RegisterType<ControllerA, ControllerA>(
    new InjectionConstructor(                        // Explicitly specify a constructor
        new ResolvedParameter<HttpClient>("ClientA") // Resolve parameter of type HttpClient using name "ClientA"
    )
);
container.RegisterType<ControllerB, ControllerB>(
    new InjectionConstructor(                        // Explicitly specify a constructor
        new ResolvedParameter<HttpClient>("ClientB") // Resolve parameter of type HttpClient using name "ClientB"
    )
);

Utiliser une usine

Si votre conteneur IoC ne dispose d'aucune capacité pour gérer les enregistrements nommés, vous pouvez injecter une fabrique et laisser le contrôleur décider de la manière d'obtenir l'instance. Voici un exemple très simple:

class HttpClientFactory : IHttpClientFactory
{
    private readonly Dictionary<string, HttpClient> _clients;

    public void Register(string name, HttpClient client)
    {
        _clients[name] = client;
    }

    public HttpClient Resolve(string name)
    {
        return _clients[name];
    }
}

Et dans vos contrôleurs:

class ControllerA
{
    private readonly HttpClient _httpClient;

    public ControllerA(IHttpClientFactory factory)
    {
        _httpClient = factory.Resolve("ClientA");
    }
}

Et dans votre composition racine:

var factory = new HttpClientFactory();
factory.Register("ClientA", new HttpClient());
factory.Register("ClientB", new HttpClient());
container.AddSingleton<IHttpClientFactory>(factory);
4
John Wu

En réalité, le consommateur du service ne devrait pas se soucier de l'implémentation de l'instance qu'il utilise. Dans votre cas, je ne vois aucune raison d'enregistrer manuellement de nombreuses instances différentes de HttpClient. Vous pouvez enregistrer le type une fois et toute instance consommatrice ayant besoin d'une instance obtiendra sa propre instance de HttpClient. Vous pouvez le faire avec AddTransient.

La méthode AddTransient est utilisée pour mapper des types abstraits à des services concrets qui sont instanciés séparément pour chaque objet qui en a besoin

services.AddTransient<HttpClient, HttpClient>();
0
Igor