.NET Core allows to lazily read settings from configuration file, de-serialize it to a POCO and register that POCO in built-in DI container with one line of code:
You can just mock IOptions if you want to unit test services that depend on it. See this question and its answers. Depending on IOptions is still just depending on an interface, so it's not (or shouldn't be) making your code harder to test.
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:
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.