9

.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:

public void ConfigureServices(IServiceCollection services) { services.Configure<MyOptions>(Configuration.GetSection("MySection")); } 

Then any consumer can resolve IOptions<MyOptions> to access that POCO:

public HomeController(IOptions<MyOptions> optionsAccessor) { MyOptions options = optionsAccessor.Value; } 

This approach has the significant disadvantages:

  • Unnecessary dependency from Microsoft.Extensions.Options package:

  • Mocking, testing and explicit instance creation become a bit more verbose.

What is the easiest solution to resolve options without IOptions<T>?

1
  • @Set, I read that article stackoverflow.blog/2011/07/01/… and decided to try. The result doesn't look suitable for a good documentation. Commented Apr 30, 2017 at 6:11

2 Answers 2

10

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.

Sign up to request clarification or add additional context in comments.

1 Comment

In ASP.NET Core 1.1 and higher, you can use configuration.GetSection("App").Get<TOptions> instead of configuration.Bind(). I find the new approach a little easier to read. learn.microsoft.com/en-us/aspnet/core/fundamentals/…
0

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.

3 Comments

Yes, of course. As I said, it is just more verbose. And it still doesn't give a reason for IOptions in constructor.
Moreover, it is not an answer to question.
Fair enough. 😀

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.