Read and use settings from appsettings.json without IOptions <T>?
.NET Core allows you to lazily read settings from a config file, de-serialize it to POCO, and register that POCO in an embedded DI with one line of code:
public void ConfigureServices(IServiceCollection services)
{
services.Configure<MyOptions>(Configuration.GetSection("MySection"));
}
Then any user can authorize IOptions<MyOptions>
to access this POCO:
public HomeController(IOptions<MyOptions> optionsAccessor)
{
MyOptions options = optionsAccessor.Value;
}
This approach has significant disadvantages:
-
Unnecessary package dependency
Microsoft.Extensions.Options
: -
Docking, testing, and explicit instantiation get a little more verbose.
What's the simplest solution for solving parameters without IOptions<T>
?
source to share
Disable options when calling configuration.Get<TOptions>
or configuration.Bind
and register the POCO in the DI container explicitly as a 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's hint for .Get<TOptions>()
.
Then permission is IOptions<T>
no longer required and the class dependencies become clear:
public HomeController(MyOptions options)
{
_options = options;
}
FYI: Reading from an external service (database, etc.) is also possible like this:
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>()));
}
Notes:
- Singleton is not lazy (it is always created at startup);
- In the case of registering single-user mode, any possibility of updating parameters at runtime is lost (.NET Core natively supports reloading files when setting parameters
reloadOnChange
:).AddJsonFile("appsettings.json", false, reloadOnChange: true)
;
If you really need to reload the file and still don't want to use IOptions
, consider a transitional solution. Of course, permission per request can lead to significant performance degradation.
source to share
You can just mock IOptions if you want to unit test the services that depend on it. See this question and its answers . Depending on the IOptions still only depends on the interface, so it shouldn't (or shouldn't) make your code more difficult to test.
source to share