Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Resolving instances with ASP.NET Core DI from within ConfigureServices

How do I manually resolve a type using the ASP.NET Core MVC built-in dependency injection framework?

Setting up the container is easy enough:

public void ConfigureServices(IServiceCollection services) {     // ...      services.AddTransient<ISomeService, SomeConcreteService>(); } 

But how can I resolve ISomeService without performing injection? For example, I want to do this:

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

There are no such methods in IServiceCollection.

like image 601
Dave New Avatar asked Sep 08 '15 13:09

Dave New


People also ask

How can dependency injection be resolved?

Resolve dependencies using IServiceProvider You can use the IServiceCollection interface to create a dependency injection container. Once the container has been created, the IServiceCollection instance is composed into an IServiceProvider instance. You can use this instance to resolve services.

How do I get an instance of IServiceProvider in .NET Core?

An instance of IServiceProvider itself can be obtained by calling a BuildServiceProvider method of an IServiceCollection. IServiceCollection is a parameter of ConfigureServices method in a Startup class. It seems to be magically called with an instance of IServiceCollection by the framework.

How does DI work in .NET Core?

ASP.NET Core supports the dependency injection (DI) software design pattern, which is a technique for achieving Inversion of Control (IoC) between classes and their dependencies. For more information specific to dependency injection within MVC controllers, see Dependency injection into controllers in ASP.NET Core.

What is difference between AddTransient and AddScoped?

AddTransient() - This method creates a Transient service. A new instance of a Transient service is created each time it is requested. AddScoped() - This method creates a Scoped service. A new instance of a Scoped service is created once per request within the scope.


2 Answers

The IServiceCollection interface is used for building a dependency injection container. After it's fully built, it gets composed to an IServiceProvider instance which you can use to resolve services. You can inject an IServiceProvider into any class. The IApplicationBuilder and HttpContext classes can provide the service provider as well, via their ApplicationServices or RequestServices properties respectively.

IServiceProvider defines a GetService(Type type) method to resolve a service:

var service = (IFooService)serviceProvider.GetService(typeof(IFooService)); 

There are also several convenience extension methods available, such as serviceProvider.GetService<IFooService>() (add a using for Microsoft.Extensions.DependencyInjection).

Resolving services inside the startup class

Injecting dependencies

The runtime's hosting service provider can inject certain services into the constructor of the Startup class, such as IConfiguration, IWebHostEnvironment (IHostingEnvironment in pre-3.0 versions), ILoggerFactory and IServiceProvider. Note that the latter is an instance built by the hosting layer and contains only the essential services for starting up an application.

The ConfigureServices() method does not allow injecting services, it only accepts an IServiceCollection argument. This makes sense because ConfigureServices() is where you register the services required by your application. However you can use services injected in the startup's constructor here, for example:

public Startup(IConfiguration configuration) {     Configuration = configuration; }  public IConfiguration Configuration { get; }  public void ConfigureServices(IServiceCollection services) {     // Use Configuration here } 

Any services registered in ConfigureServices() can then be injected into the Configure() method; you can add an arbitrary number of services after the IApplicationBuilder parameter:

public void ConfigureServices(IServiceCollection services) {     services.AddScoped<IFooService>(); }  public void Configure(IApplicationBuilder app, IFooService fooService) {     fooService.Bar(); } 

Manually resolving dependencies

If you need to manually resolve services, you should preferably use the ApplicationServices provided by IApplicationBuilder in the Configure() method:

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

It is possible to pass and directly use an IServiceProvider in the constructor of your Startup class, but as above this will contain a limited subset of services, and thus has limited utility:

public Startup(IServiceProvider serviceProvider) {     var hostingEnv = serviceProvider.GetService<IWebHostEnvironment>(); } 

If you must resolve services in the ConfigureServices() method, a different approach is required. You can build an intermediate IServiceProvider from the IServiceCollection instance which contains the services which have been registered up to that point:

public void ConfigureServices(IServiceCollection services) {     services.AddSingleton<IFooService, FooService>();      // Build the intermediate service provider     var sp = services.BuildServiceProvider();      // This will succeed.     var fooService = sp.GetService<IFooService>();     // This will fail (return null), as IBarService hasn't been registered yet.     var barService = sp.GetService<IBarService>(); } 

Please note: Generally you should avoid resolving services inside the ConfigureServices() method, as this is actually the place where you're configuring the application services. Sometimes you just need access to an IOptions<MyOptions> instance. You can accomplish this by binding the values from the IConfiguration instance to an instance of MyOptions (which is essentially what the options framework does):

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

Or use an overload for AddSingleton/AddScoped/AddTransient:

// Works for AddScoped and AddTransient as well services.AddSingleton<IBarService>(sp => {     var fooService = sp.GetRequiredService<IFooService>();     return new BarService(fooService); } 

Manually resolving services (aka Service Locator) is generally considered an anti-pattern. While it has its use-cases (for frameworks and/or infrastructure layers), you should avoid it as much as possible.

like image 155
Henk Mollema Avatar answered Sep 23 '22 17:09

Henk Mollema


Manually resolving instances involves using the IServiceProvider interface:

Resolving Dependency in Startup.ConfigureServices

public void ConfigureServices(IServiceCollection services) {     services.AddTransient<IMyService, MyService>();      var serviceProvider = services.BuildServiceProvider();     var service = serviceProvider.GetService<IMyService>(); } 

Resolving Dependencies in 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>();      // ... } 

Resolving Dependencies in Startup.Configure in ASP.NET Core 3

public void Configure(     IApplicationBuilder application,     IWebHostEnvironment webHostEnvironment) {     application.ApplicationServices.GetService<MyService>(); } 

Using Runtime Injected Services

Some types can be injected as method parameters:

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)     {     } } 

Resolving Dependencies in Controller Actions

[HttpGet("/some-action")] public string SomeAction([FromServices] IMyService myService) => "Hello"; 
like image 28
Muhammad Rehan Saeed Avatar answered Sep 19 '22 17:09

Muhammad Rehan Saeed