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>

?

+2


source to share


2 answers


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.

+4


source


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.



+1


source







All Articles