4

Straightforward question is: are Microsoft.Extensions.Options.IOptions meant to be used only within the context of umbrella app (web app in this case) or in class libraries also?

Example:

In a n-layered, asp.net core app we have services layer that is dependant on some settings coming from appsettings.json file.

What we first started with is something along these lines in Startup.cs:

  services.Configure<Services.Options.XOptions>(options =>
  {
    options.OptionProperty1 = Configuration["OptionXSection:OptionXProperty"];
  });

And then in service constructor:

ServiceConstructor(IOptions<XOptions> xOptions){}

But that assumes that in our Service layer we have dependecy on Microsoft.Extensions.Options.

We're not sure if this is recomended way or is there some better practice?

It just feels a bit awkward our services class library should be aware of DI container implementation.

16
  • it is perfectly legit to use IOptions wherever you find them useful in my opinion Commented Jul 8, 2017 at 15:24
  • what happens if you want to use your service tomorrow in some other umbrella app that uses some DI container other than .net core defaults one? Commented Jul 8, 2017 at 15:25
  • service class doesn't need to be aware of DI, it just has a consturcotr dependency, service itself should also be added by DI not newed up. DI works through all the layers Commented Jul 8, 2017 at 15:25
  • 1
    none of your classes should be aware of the DI container except Startup.cs where it is wired up you can easily switch to other DI containers instead of the default one. Having a constructor dependency doe snot imply awareness of DI Commented Jul 8, 2017 at 15:26
  • 1
    @deezg in 99.9% I don't need to reload the appsettings.json, so I don't use IOptions<T>. You could find a similar question conserning advantages and drawbacks of such approach here: stackoverflow.com/questions/43679665/… Commented Jul 9, 2017 at 10:48

1 Answer 1

6

You can register POCO settings for injection too, but you lose some functionalities related to when the appsettings.json gets edited.

services.AddTransient<XOptions>(
    provider => provider.GetRequiredService<IOptionsSnapshot<XOptions>>().Value);

Now when you inject XOptions in constructor, you will get the class. But when your edit your appsettings.json, the value won't be updated until the next time it's resolved which for scoped services would be on next request and singleton services never.

On other side injecting IOptionsSnapshot<T> .Value will always get you the current settings, even when appsettings.json is reloaded (assuming you registered it with .AddJsonFile("appsettings.json", reloadOnSave: true)).

The obvious reason to keep the functionality w/o pulling Microsoft.Extensions.Options package into your service/domain layer will be create your own interface and implementation.

// in your shared service/domain assembly
public interface ISettingsSnapshot<T> where T : class
{
    T Value { get; }
}

and implement it on the application side (outside of your services/domain assemblies), i.e. MyProject.Web (where ASP.NET Core and the composition root is)

public class OptionsSnapshotWrapper<T> : ISettingsSnapshot<T>
{
    private readonly IOptionsSnapshot<T> snapshot;

    public OptionsSnapshotWrapper(IOptionsSnapshot<T> snapshot) 
    {
        this.snapshot = snapshot ?? throw new ArgumentNullException(nameof(snapshot));
    }

    public T Value => snapshot.Value;
}

and register it as

services.AddSingleton(typeof(ISettingsSnapshot<>), typeof(OptionsSnapshotWrapper<T>));

Now you have removed your dependency on IOptions<T> and IOptionsSnapshot<T> from your services but retain all up advantages of it like updating options when appsettings.json is edited. When you change DI, just replace OptionsSnapshotWrapper<T> with your new implementation.

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.