Resolving Instances with ASP.NET Core Di from Within Configureservices

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:

  1. root.GetService<Singleton>(); - every time it's called returns same object instance.
  2. root.GetService<Scoped>(); - every time it's called returns same object instance.
  3. 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;
  1. 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).
  2. sp1.GetService<Scoped>(); - every time it's called returns same object instance, but not the same instance that root returns. Object instance is cached on the current scope. Every scope creates/caches it's own scoped instance.
  3. 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



Leave a reply



Submit