How to Resolve Ioptions Instance Inside Configureservices

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.

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 get IOptions in ConfigureServices method or pass IOptions into extension method?

For binding data from appsettings.json to Model, you could follow steps below:

  1. Appsettings.json content

    {
    "Logging": {
    "IncludeScopes": false,
    "LogLevel": {
    "Default": "Warning"
    }
    },
    "JWT": {
    "Issuer": "I",
    "Key": "K"
    }
    }
  2. JWT Options

    public class JwtOptions
    {
    public string Issuer { get; set; }
    public string Key { get; set; }
    }
  3. Startup.cs

    public void ConfigureServices(IServiceCollection services)
    {
    services.Configure<JwtOptions>(Configuration.GetSection("JWT"));
    var serviceProvider = services.BuildServiceProvider();
    var opt = serviceProvider.GetRequiredService<IOptions<JwtOptions>>().Value;
    services.AddJwtAuthentication(opt.Issuer, opt.Key);
    services.AddMvc();
    }
  4. One more option to pass JwtOptions directly.

    public void ConfigureServices(IServiceCollection services)
    {
    services.Configure<JwtOptions>(Configuration.GetSection("JWT"));
    var serviceProvider = services.BuildServiceProvider();
    var opt = serviceProvider.GetRequiredService<IOptions<JwtOptions>>().Value;
    services.AddJwtAuthentication(opt);

    services.AddMvc();
    }
  5. Change the extension method.

    public static IServiceCollection AddJwtAuthentication(this IServiceCollection services, JwtOptions opt)

Passing IOptions T to method in StartUp Configuration

It is not clear why you want to create an object of class in Startup class. But you can solve your problem as following.

IServiceCollection is used only for create the dependency graph but to resolve the actual dependencies at runtime, ServiceProvider is needed.

To build ServiceProvider, BuildServiceProvider method needs to be called on ServiceCollection. You can see that in the code below.

public void ConfigureServices(IServiceCollection services)
{
//Register the configuration section in the service collection.
services.Configure<BitBucketSettings>(Configuration.GetSection("BitBucketOptions");

// Register the class in service collection.
services.AddScoped<PlayClass, PlayClass>();

// Build Service Provider
var sp = services.BuildServiceProvider();

// Resolve instance or PlayClass from service builder.
var pc = sp.GetService<PlayClass>();

// Call method on instance of PlayClass
pc.MyMethod();
}

I hope this will help you solve your issue.

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.

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

Accessing IOptions and DbContext within ConfigureServices

You can register your healthcheck like this:

services.AddHealthChecks()
.AddCheck<ExampleHealthCheck>("Database");

And then just inject your DbContext into ExampleHealthCheck class, which has to implement IHealthCheck interface

why IOptions is getting resolved even if not registered

The options framework is set up by the default host builder as part of its setup, so you do not need to AddOptions() yourself. This however also ensures that you can use IOptions<T> wherever you want since the framework will provide that exact options object for you.

The way options work is that the framework will always give you a T (as long as it can construct one). When you do set up configuration using e.g. AddOptions<T> or Configure<T>, what actually happens is that a configuration action gets registered for that type T. And when an IOptions<T> is later resolved, all those registered actions will run in the sequence they are registered.

This means that it’s valid to not have configured an options type. In that case, the default values from the object will be used. Of course, this also means that you are not able to detect whether you have actually configured the options type and whether the configuration is actually valid. This usually has to be done when you use the values.

For example, if you require Config1 to be configured, you should explicitly look for it:

public HelloWorldController(IOptions<UploadConfig> config)
{
if (string.IsNullOrEmpty(config.Value.Config1))
throw ArgumentException("Config1 is not configured properly");
}

An alternative would be to register a validation action for a type using OptionsBuilder.Validate. This will then be called automatically when you resovle the options object to validate the containing value. That way, you can have the validation set up in a central location:

services.AddOptions<UploadConfig>()
.Bind(Configuration.GetSection("UploadConfig"))
.Validate(c => !string.IsNullOrEmpty(c.Config1));

Unfortunately, this also means that you can only detect these problems when you actually use the values, which can be missed if you are not testing your application thoroughly. A way around this would be to resolve the options once when the application starts and validate them there.

For example, you could just inject your IOptions<T> within your startup’s Configure method:

public void Configure(IApplicationBuilder app, IHostingEnvironment env, IOptions<UploadConfig> uploadOptions)
{
// since the options are injected here, they will be constructed and automatically
// validated if you have configured a validate action

// …
app.UseMvc();
}

Alternatively, if you have multiple options you want to validate and if you want to run logic that does not fit into the validation action, you could also create a service that validates them:

public class OptionsValidator
{
private readonly IOptions<UploadConfig> uploadOptions;
public OptionsValidator(IOptions<UploadConfig> uploadOptions)
{
_uploadOptions = uploadOptions;
}

public void Validate()
{
if (string.IsNullOrEmpty(_uploadOptions.Value.Config1))
throw Exception("Upload options are not configured properly");
}
}

And then inject that in your Configure:

public void Configure(IApplicationBuilder app, IHostingEnvironment env, OptionsValidator optionsValidator)
{
// validate options explicitly
optionsValidator.Validate();

// …
app.UseMvc();
}

Whatever you do, keep also in mind that by default the configuration sources are configured to support updating the configuration at run-time. So you will always have a situation in which a configuration can be invalid temporarily at run-time.



Related Topics



Leave a reply



Submit