web-dev-qa-db-fra.com

Résolution d'instances avec ASP.NET Core DI

Comment résoudre manuellement un type à l'aide du framework d'injection de dépendance intégré ASP.NET Core MVC?

La mise en place du conteneur est assez simple:

public void ConfigureServices(IServiceCollection services)
{
    // ...

    services.AddTransient<ISomeService, SomeConcreteService>();
}

Mais comment puis-je résoudre ISomeService sans effectuer d’injection? Par exemple, je veux faire ceci:

ISomeService service = services.Resolve<ISomeService>();

Il n'y a pas de telles méthodes dans IServiceCollection.

213
Dave New

L'interface IServiceCollection est utilisée pour construire un conteneur d'injection de dépendance. Une fois la compilation terminée, il se compose d'une instance IServiceProvider que vous pouvez utiliser pour résoudre les services. Vous pouvez injecter un IServiceProvider dans n’importe quelle classe. Les classes IApplicationBuilder et HttpContext peuvent également fournir le fournisseur de services via les propriétés ApplicationServices ou RequestServices.

IServiceProvider définit une méthode GetService(Type type) pour résoudre un service:

_var service = (IFooService)serviceProvider.GetService(typeof(IFooService));
_

Il existe également plusieurs méthodes d'extension pratiques, telles que serviceProvider.GetService<IFooService>() (ajoutez un using pour _Microsoft.Extensions.DependencyInjection_).

Résolution de services dans la classe de démarrage

Injecter des dépendances

Le moteur d'exécution peut injecter des services dans le constructeur de la classe Startup, tels que IHostingEnvironment, IConfiguration et IServiceProvider. Veuillez noter que ce fournisseur de services est une instance créée par la couche d'hébergement et ne contient que les services permettant de démarrer une application.

Les services peuvent également être injectés dans la méthode Configure(). Vous pouvez ajouter une liste arbitraire de paramètres après le paramètre IApplicationBuilder. Vous pouvez également injecter ici vos propres services qui sont enregistrés dans la méthode ConfigureServices(), ils seront résolus à partir du fournisseur de service de l'application plutôt que de la méthode fournisseur de services d'hébergement .

_public void Configure(IApplicationBuilder app, IFooService fooService)
{
   // ...
}
_

La méthode ConfigureServices() ne permet toutefois pas l'injection de services, elle n'accepte qu'un argument IServiceCollection. C'est la méthode où vous configurez votre conteneur d'injection de dépendance d'application. Vous pouvez utiliser les services injectés dans le constructeur de la startup ici. Par exemple:

_public Startup(IConfiguration configuration)
{
    Configuration = configuration;
}

public IConfiguration Configuration { get; }

public void ConfigureServices(IServiceCollection services)
{
    // Use Configuration here
}
_

Résolution manuelle des dépendances

Si vous voulez résoudre manuellement les services, vous pouvez laisser le runtime injecter une instance IServiceProvider dans le constructeur ou utiliser le ApplicationServices fourni par IApplicationBuilder dans la méthode Configure():

_public Startup(IServiceProvider serviceProvider)
{
    var hostingEnv = serviceProvider.GetService<IHostingEnvironment>();
}
_

ou

_public void Configure(IApplicationBuilder app)
{
    var serviceProvider = app.ApplicationServices;
    var hostingEnv = serviceProvider.GetService<IHostingEnvironment>();
}
_

Toutefois, si vous devez résoudre des services dans la méthode ConfigureServices(), vous devez adopter une approche différente. Vous pouvez construire une IServiceProvider intermédiaire à partir d'une instance de IServiceCollection contenant les services enregistrés jusque-là:

_public void ConfigureServices(IServiceCollection services)
{
    services.AddSingleton<IFooService, FooService>();

    // Build the intermediate service provider
    var sp = services.BuildServiceProvider();
    var fooService = sp.GetService<IFooService>();
}
_

Vous avez besoin du package Microsoft.Extensions.DependencyInjection pour cela.

Remarque:
En règle générale, vous ne devriez pas résoudre les services dans la méthode ConfigureServices(), car il s’agit en fait de l’endroit où vous configurez les services de l’application. Parfois, vous avez simplement besoin d'un accès à une instance _IOptions<MyOptions>_. Vous pouvez accomplir cela en liant les valeurs de l'instance IConfiguration à une instance de MyOptions (qui correspond essentiellement à la structure des options):

_public void ConfigureServices(IServiceCollection services)
{
    var myOptions = new MyOptions();
    Configuration.GetSection("SomeSection").Bind(myOptions);
}
_

La résolution manuelle des services (ou Service Locator) est généralement connue sous le nom d’anti-pattern . Bien qu’il ait ses cas d’utilisation (pour les cadres et/ou les couches d’infrastructure), vous devez l’éviter autant que possible.

355
Henk Mollema

La résolution manuelle d'instances implique l'utilisation de l'interface IServiceProvider :

Résolution des dépendances dans Startup.ConfigureServices

public void ConfigureServices(IServiceCollection services)
{
    services.AddTransient<IMyService, MyService>();

    var serviceProvider = services.BuildServiceProvider();
    var service = serviceProvider.GetService<IMyService>();
}

Résolution des dépendances dans Startup.Configure

public void Configure(
    IApplicationBuilder application,
    IServiceProvider serviceProvider)
{
    // By type.
    var service1 = (MyService)serviceProvider.GetService(typeof(MyService));

    // Using extension method.
    var service2 = serviceProvider.GetService<MyService>();

    // ...
}

Utilisation des services injectés à l'exécution

Certains types peuvent être injectés en tant que paramètres de méthode:

public class Startup
{
    public Startup(
        IHostingEnvironment hostingEnvironment,
        ILoggerFactory loggerFactory)
    {
    }

    public void ConfigureServices(
        IServiceCollection services)
    {
    }

    public void Configure(
        IApplicationBuilder application,
        IHostingEnvironment hostingEnvironment,
        IServiceProvider serviceProvider,
        ILoggerFactory loggerfactory,
        IApplicationLifetime applicationLifetime)
    {
    }
}

Résolution des dépendances dans les actions du contrôleur

[HttpGet("/some-action")]
public string SomeAction([FromServices] IMyService myService) => "Hello";
75

Si vous générez une application avec un modèle, vous allez avoir quelque chose comme ceci sur la classe Startup:

public void ConfigureServices(IServiceCollection services)
{
    // Add framework services.
    services.AddApplicationInsightsTelemetry(Configuration);

    services.AddMvc();
}

Vous pouvez ensuite y ajouter des dépendances, par exemple:

services.AddTransient<ITestService, TestService>();

Si vous voulez accéder à ITestService sur votre contrôleur, vous pouvez ajouter IServiceProvider sur le constructeur et celui-ci sera injecté:

public HomeController(IServiceProvider serviceProvider)

Ensuite, vous pouvez résoudre le service que vous avez ajouté:

var service = serviceProvider.GetService<ITestService>();

Notez que pour utiliser la version générique, vous devez inclure l'espace de noms avec les extensions:

using Microsoft.Extensions.DependencyInjection;

ITestService.cs

public interface ITestService
{
    int GenerateRandom();
}

TestService.cs

public class TestService : ITestService
{
    public int GenerateRandom()
    {
        return 4;
    }
}

Startup.cs (ConfigureServices)

public void ConfigureServices(IServiceCollection services)
{
    services.AddApplicationInsightsTelemetry(Configuration);
    services.AddMvc();

    services.AddTransient<ITestService, TestService>();
}

HomeController.cs

using Microsoft.Extensions.DependencyInjection;

namespace Core.Controllers
{
    public class HomeController : Controller
    {
        public HomeController(IServiceProvider serviceProvider)
        {
            var service = serviceProvider.GetService<ITestService>();
            int rnd = service.GenerateRandom();
        }
13
BrunoLM

Si vous avez juste besoin de résoudre une dépendance pour la transmettre au constructeur d'une autre dépendance que vous enregistrez, vous pouvez le faire.

Supposons que vous disposiez d'un service comprenant une chaîne et un ISomeService.

public class AnotherService : IAnotherService
{
    public AnotherService(ISomeService someService, string serviceUrl)
    {
        ...
    }
}

Lorsque vous vous enregistrez dans Startup.cs, vous devez procéder comme suit:

services.AddScoped<IAnotherService>(ctx => 
      new AnotherService(ctx.GetService<ISomeService>(), "https://someservice.com/")
);
2
raterus

Vous pouvez injecter des dépendances dans des attributs tels que AuthorizeAttribute de cette manière

var someservice = (ISomeService)context.HttpContext.RequestServices.GetService(typeof(ISomeService));
1
Bora Aydın