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
.
ConfigureServices()
method (with IServiceCollection
) or just anywhere in the application?
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.
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";
GetService
which is generic is an extension method in Microsoft.Extensions.DependencyInjection
namespace.
If you generate an application with a template you are going to have something like this on the Startup
class:
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddApplicationInsightsTelemetry(Configuration);
services.AddMvc();
}
You can then add dependencies there, for example:
services.AddTransient<ITestService, TestService>();
If you want to access ITestService
on your controller you can add IServiceProvider
on the constructor and it will be injected:
public HomeController(IServiceProvider serviceProvider)
Then you can resolve the service you added:
var service = serviceProvider.GetService<ITestService>();
Note that to use the generic version you have to include the namespace with the 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();
}
If you just need to resolve one dependency for the purpose of passing it to the constructor of another dependency you are registering, you can do this.
Let's say you had a service that took in a string and an ISomeService.
public class AnotherService : IAnotherService
{
public AnotherService(ISomeService someService, string serviceUrl)
{
...
}
}
When you go to register this inside Startup.cs, you'll need to do this:
services.AddScoped<IAnotherService>(ctx =>
new AnotherService(ctx.GetService<ISomeService>(), "https://someservice.com/")
);
ISomeService
was still null for me.
services.AddScoped<IAnotherService>(ctx => ActivatorUtilities.CreateInstance<AnotherService>(ctx, "https://someservice.com/") );
should be the preferred solution.
You can inject dependencies in attributes like AuthorizeAttribute in this way
var someservice = (ISomeService)context.HttpContext.RequestServices.GetService(typeof(ISomeService));
var someservice = context.HttpContext.RequestServices.GetService<ISomeService>();
I know this is an old question but I'm astonished that a rather obvious and disgusting hack isn't here.
You can exploit the ability to define your own ctor function to grab necessary values out of your services as you define them... obviously this would be ran every time the service was requested unless you explicitly remove/clear and re-add the definition of this service within the first construction of the exploiting ctor.
This method has the advantage of not requiring you to build the service tree, or use it, during the configuration of the service. You are still defining how services will be configured.
public void ConfigureServices(IServiceCollection services)
{
//Prey this doesn't get GC'd or promote to a static class var
string? somevalue = null;
services.AddSingleton<IServiceINeedToUse, ServiceINeedToUse>(scope => {
//create service you need
var service = new ServiceINeedToUse(scope.GetService<IDependantService>())
//get the values you need
somevalue = somevalue ?? service.MyDirtyHack();
//return the instance
return service;
});
services.AddTransient<IOtherService, OtherService>(scope => {
//Explicitly ensuring the ctor function above is called, and also showcasing why this is an anti-pattern.
scope.GetService<IServiceINeedToUse>();
//TODO: Clean up both the IServiceINeedToUse and IOtherService configuration here, then somehow rebuild the service tree.
//Wow!
return new OtherService(somevalue);
});
}
The way to fix this pattern would be to give OtherService
an explicit dependency on IServiceINeedToUse
, rather than either implicitly depending on it or its method's return value... or resolving that dependency explicitly in some other fashion.
You can inject dependencies using IApplicationBuilder instance in this way
public void Configure(IApplicationBuilder app)
{
//---------- Your code
using (var serviceScope = app.ApplicationServices.GetRequiredService<IServiceScopeFactory>().CreateScope())
{
var resultLogic = serviceScope.ServiceProvider.GetService<IResultLogic>();
resultLogic.YourMethod();
}
//---------- Your code
}
public void ConfigureServices(IServiceCollection services) {
services.AddSingleton<ISelfServiceConfigLoad, SelfServiceConfigLoader>();
var sp = services.BuildServiceProvider();
var configservice = sp.GetServices<ISelfServiceConfigLoad>();
services.AddSingleton<IExtractor, ConfigExtractor>( sp =>
{
var con = sp.GetRequiredService<ISelfServiceConfigLoad>();
var config = con.Load();
return new ConfigExtractor(config.Result);
});
services.AddSingleton<IProcessor<EventMessage>, SelfServiceProcessor>();
services.AddTransient<ISolrPush, SolrDataPush>();
services.AddSingleton<IAPICaller<string, string>, ApiRestCaller<string, string>>();
services.AddSingleton<IDataRetriever<SelfServiceApiRequest, IDictionary<string, object>>, SelfServiceDataRetriever>();
}
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddDbContext<ConfigurationRepository>(options =>
options.UseSqlServer(Configuration.GetConnectionString("SqlConnectionString")));
services.AddScoped<IConfigurationBL, ConfigurationBL>();
services.AddScoped<IConfigurationRepository, ConfigurationRepository>();
}
Success story sharing
IServiceCollection
injected, some class that is being created manually(out of middle ware scope), a scheduler in my case, which periodically needs some services to generate and send an email.ConfigureServices
and that service is a singleton it will be a different singleton to the one yourController
s use! I assume this is because it uses a differentIServiceProvider
- to avoid this do NOT resolve viaBuildServiceProvider
and instead move your lookup of the singleton fromConfigureServices
toConfigure(..other params, IServiceProvider serviceProvider)
inStartup.cs
IServiceProvider
instance it will create a new singleton instance. You could avoid this by returning the service provider instance from theConfigureServices
method so that will be the container your application uses as well.collection.BuildServiceProvider();
was what I needed, thanks!