Resolving instances with ASP.NET Core DI from within ConfigureServices
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.
How to properly resolve instance in ConfigureServices()?
After checking a hunch, the event delegate's event args is TicketReceivedContext Class
which can give you access to the service provider within the HttpContext
.
//...
services.AddOpenIdConnect("Auth0", options => {
options.Events = new OpenIdConnectEvents {
OnTicketReceived = async e => {
//How do I get instances of my services (eg: ILogger<Startup>) at this point?
IServiceProvider serviceProvider = e.HttpContext.RequestServices;
var logger = serviceProvider.GetService<ILogger<Startup>>();
//...
},
};
});
//...
A similar format can also be followed for the other events / handlers
How to resolve IOptions instance inside ConfigureServices?
If you need to resolve service using the service provider manually you can use this AddSingleton/AddScoped/AddTransient
overload:
// Works for AddScoped and AddTransient as well
services.AddSingleton<IBarService>(sp =>
{
var fooService = sp.GetRequiredService<IFooService>();
return new BarService(fooService);
}
If you really want to, you can build an intermediate service provider using the BuildServiceProvider()
method on the IServiceCollection
:
public void ConfigureService(IServiceCollection services)
{
// Configure the services
services.AddTransient<IFooService, FooServiceImpl>();
services.Configure<AppSettings>(configuration.GetSection(nameof(AppSettings)));
// Build an intermediate service provider
var sp = services.BuildServiceProvider();
// Resolve the services from the service provider
var fooService = sp.GetService<IFooService>();
var options = sp.GetService<IOptions<AppSettings>>();
}
You need the Microsoft.Extensions.DependencyInjection
package for this.
However, please note that this results in multiple service provider instances which may in turn result in multiple singleton instances.
In the case where you just need to bind some options in ConfigureServices
, you can also use the Bind
method:
var appSettings = new AppSettings();
configuration.GetSection(nameof(AppSettings)).Bind(appSettings);
This functionality is available through the Microsoft.Extensions.Configuration.Binder
package.
ASP.Net Core 5 ConfigureServices using reflection based on instance created by a service
If you are only registering IEntityDtoMappingProvider so that you use it to build your mapping component then maybe you shouldn't register it. This sort of one time configuration is often best done outside the scope of the container itself. As you suggested you can probably just remove the interface entirely and use the concrete class directly.
Same goes for things like logger configuration.
ApplicationServices resolves a different scoped instance in net core?
TL;DR;
IFoo foo
is resolved using myServiceProvider.GetService<IFoo>()
before both get passed into Configure
method. So you're resolving same IFoo
instance from the same myServiceProvider
instance for the 2nd time.
app.ApplicationServices
is special root service provider of the application. Parent of myServiceProvider
. Thus it resolves different IFoo
.
**Default behavior of app.ApplicationServices
should be to throw exception when you try to resolve scoped service.
LONGER EXPLANATION
If you have three dummy classes:
class Singleton { }
class Scoped { }
class Transient { }
And you register them as such in IServiceConfiguration
:
public static void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<Singleton>();
services.AddScoped<Scoped>();
services.AddTransient<Transient>();
}
Now, you create your "root" IServiceProvider
from IServiceCollection
- in command-line app, it would look like this:
ServiceCollection sc = new ServiceCollection();
ConfigureServices(sc);
ServiceProvider root = sc.BuildServiceProvider();
ROOT SERVICE PROVIDER BEHAVIOR (equivalent to app.ApplicationServices
):
If you now test root
:
root.GetService<Singleton>();
- every time it's called returns same object instance.root.GetService<Scoped>();
- every time it's called returns same object instance.root.GetService<Transient>();
every time it's called returns new object instance.
CHILD-SCOPE SERVICE PROVIDER BEHAVIOR (eg: IServiceProvider
in Configure
method):
If you now create child scope and use it's own IServiceProvider
:
IServiceScope scope1 = root.CreateScope();
IServiceProvider sp1 = scope1.ServiceProvider;
sp1.GetService<Singleton>();
- every time it's called returns same object instance whichroot.GetService<Singleton>();
returns.Singleton
is the same instance no matter from which scope you call it. It is resolved climbing the hierarchy of scopes back to the root service provider (scopeless one).sp1.GetService<Scoped>();
- every time it's called returns same object instance, but not the same instance thatroot
returns. Object instance is cached on the current scope. Every scope creates/caches it's own scoped instance.sp1.GetService<Transient>();
every time it's called returns new object instance, same behavior like for the root.
root
scope is "special" only because it has no parent scope, so resolving scoped or singleton service from the root
technically does the same thing - object instance returned is cached in the root
itself.
This also explains why you cannot resolve service from IServiceCollection
directly. IServiceCollection
does not have hierarchy of scopes and caching infrastructure which IServiceProvider
has. It just contains list of ServiceDescriptor
. In addition, it would be unclear in which scope service instance should be cached.
ASP.NET Core
For ASP.NET Core root IServiceProvider
is app.ApplicationServices
. Configure
method receives first child-scope created from the root
- application-scope. For every HTTP request application-scope creates child-scope which is used to resolve all services and is itself injected in controllers and views of that HTTP request. It is also used to inject all other types in a controller constructor or a view.
IFoo
resolution
So, your foo
from Configure
method is resolved using myServiceProvider
, then they both get used as input parameters for Configure
. Framework does something like this:
ServiceProvider root = sc.BuildServiceProvider(validateScopes: true);
var appScope = root.CreateScope();
IFoo foo = appScope.ServiceProvider.GetService<IFoo>();
ConfigureServices(foo, appScope.ServiceProvider);
When you call sp.GetService<IFoo>()
inside of Configure
method it is identical to appScope.ServiceProvider.GetService<IFoo>();
that was already called from the outside. root.GetService<IFoo>()
creates different IFoo
instance, as it should.
More ASP.NET Core:
To prevent developers making mistake trying to resolve scoped
service from the app.ApplicationServices
and not realizing that it is application scoped (global), instead of being scoped to HTTP request, by default, ASP.NET Core creates root ServiceProvider
using BuildServiceProvider
overload:
ServiceProvider root = sc.BuildServiceProvider(validateScopes: true);
validateScopes: true to perform check verifying that scoped services never gets resolved from root provider; otherwise false.
However, this might depend on compatibility mode you're using. I'm guessing that's the reason why it allows you to resolve scoped service via app.ApplicationServices
.
How to correctly resolve services to use in the ConfigureServices() in ASP.NET Core 3.1?
Reference Use DI services to configure options
Configure the CORS options using DI, moving all the logic into the configure delegate
//...
// Register context
services.AddDbContext<AppDbContext>(options => {
options.UseMySql(Configuration.GetConnectionString("MySqlServerConnection"));
});
// Reister the setting provider
services.AddScoped<IAppSetting, AppSettings>();
//configure CORS options using DI
services.AddOptions<CorsOptions>()
.Configure<IServiceScopeFactory>((options, sp) => {
using(var scope = sp.CreateScope()) {
IAppSetting settings = scope.ServiceProvider.GetRequiredService<IAppSetting>();
List<string> allowedCors = new List<string>() {
setting.MainUrl.ToString()
};
if (setting.HasCustomAssetsUri) {
allowedCors.Add(settings.AssetsUrl.ToString());
}
if (settings.HasCustomPhotosUri) {
allowedCors.Add(settings.PhotosUrl.ToString());
}
options.AddPolicy("AllowSubDomainTraffic", builder => {
builder.WithOrigins(allowedCors.ToArray())
.AllowAnyHeader()
.AllowAnyMethod();
});
}
});
services.AddCors();
//...
that way everything is now deferred to when they are actually needed and you avoid having to build the service collection prematurely.
Related Topics
Is Developing in Mono Cross-Platform
Reading a C/C++ Data Structure in C# from a Byte Array
In a "Using" Block Is a SQLconnection Closed on Return or Exception
Get Connection String from App.Config
Accessing a Variable from Another Script C#
How to Split a String by a Multi-Character Delimiter in C#
Call Asynchronous Method in Constructor
What's the Difference Between Task.Start/Wait and Async/Await
Regular Expression for Finding 'Href' Value of a <A> Link
Xml Serialization - Hide Null Values
How to Get and Set Environment Variables in C#
Why Are C# 3.0 Object Initializer Constructor Parentheses Optional
Entity Framework Change Connection at Runtime
Web App Blocked While Processing Another Web App on Sharing Same Session