Deserialize options with configuration.Get<TOptions> or configuration.Bind call and register a POCO in DI container explicitly as singleton:
public void ConfigureServices(IServiceCollection services) { services.AddSingletonFromFile<MyOptions>(Configuration.GetSection("MySection")); } //... public static IServiceCollection AddSingletonFromFile<TOptions>( this IServiceCollection services, IConfiguration configuration) where TOptions : class, new() { //POCO is created with actual values TOptions options = configuration.Get<TOptions>(); services.AddSingleton(options); return services; }
UPD: thanks to @NPNelson for .Get<TOptions>() hint.
Then IOptions<T> resolving is no longer needed, and the class dependencies become clear:
public HomeController(MyOptions options) { _options = options; }
FYI: reading from an external service (database etc.) is also possible this way:
public void ConfigureServices(IServiceCollection services) { services.AddTransientFromService<OptionsReader, MyOptions>(reader => reader.GetOptions()); } //... public static void AddTransientFromService<TReader, TOptions>( this IServiceCollection services, Func<TReader, TOptions> getOptions) where TReader : class where TOptions : class { services.AddTransient(provider => getOptions(provider.GetService<TReader>())); }
Remarks:
- Singleton is not lazy (it's always instantiated at startup);
- In case of singleton registration, any ability to update options at runtime is lost (.NET Core natively supports runtime file reloading with
reloadOnChange option setup: .AddJsonFile("appsettings.json", false, reloadOnChange: true));
If you really need the file reload and you still don't want to use IOptions, consider a transient resolving. Of course, per-request resolving can lead to the significant perfomance decrease.