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
.
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
_).
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
}
_
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.
La résolution manuelle d'instances implique l'utilisation de l'interface IServiceProvider
:
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<IMyService, MyService>();
var serviceProvider = services.BuildServiceProvider();
var service = serviceProvider.GetService<IMyService>();
}
public void Configure(
IApplicationBuilder application,
IServiceProvider serviceProvider)
{
// By type.
var service1 = (MyService)serviceProvider.GetService(typeof(MyService));
// Using extension method.
var service2 = serviceProvider.GetService<MyService>();
// ...
}
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)
{
}
}
[HttpGet("/some-action")]
public string SomeAction([FromServices] IMyService myService) => "Hello";
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();
}
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/")
);
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));