In my ASP.NET Core application I bind the appsettings.json to a strongly typed class AppSettings.
public Startup(IHostingEnvironment environment)
{
var builder = new ConfigurationBuilder()
.SetBasePath(environment.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{environment.EnvironmentName}.json", optional: true, reloadOnChange: true)
.AddEnvironmentVariables();
Configuration = builder.Build();
}
public void ConfigureServices(IServiceCollection services)
{
services.Configure<AppSettings>(Configuration);
//...
}
In a singleton class I wrap this AppSettings class like this:
public class AppSettingsWrapper : IAppSettingsWrapper
{
private readonly IOptions<AppSettings> _options;
public AppSettingsAdapter(IOptions<AppSettings> options)
{
_options = options ?? throw new ArgumentNullException("Options cannot be null");
}
public SomeObject SomeConvenienceGetter()
{
//...
}
}
Now I'm struggling with reloading the AppSettings if the json file changes. I read somewhere that the class IOptionsMonitor can detect changes but it doesn't work in my case.
I tried calling the OnChange event like this for testing purposes:
public void Configure(IApplicationBuilder applicationBuilder, IOptionsMonitor<AppSettings> optionsMonitor)
{
applicationBuilder.UseStaticFiles();
applicationBuilder.UseMvc();
optionsMonitor.OnChange<AppSettings>(vals =>
{
System.Diagnostics.Debug.WriteLine(vals);
});
}
The event is never triggered when I change the json file. Has someone an idea what I can change to get the reloading mechanic to work in my scenario?
What you can do is create your wrapper class around the config class like you did in AppSettingsWrapper and inject IOptionsMonitor. Then keep a private property of your settings class. That wrapper can be injected as a singleton and the IOptionsMonitor will keep track of your changes.
public class AppSettingsWrapper
{
private AppSettings _settings;
public AppSettingsWrapper(IOptionsMonitor<AppSettings> settings)
{
_settings = settings.CurrentValue;
// Hook in on the OnChange event of the monitor
settings.OnChange(Listener);
}
private void Listener(AppSettings settings)
{
_settings = settings;
}
// Example getter
public string ExampleOtherApiUrl => _settings.ExampleOtherApiUrl;
}
Then register your wrapper class as a singleton
services.AddSingleton(sp => new AppSettingsWrapper(sp.GetService<IOptionsMonitor<AppSettings>>()));
You need to inject IOptionsSnapshot<AppSettings>
to get the reload working.
Unfortunately you cannot load the IOptionsSnapshot
into a Singleton service. IOptionsSnapshot
is a Scoped service so you can only reference it in a Scoped or Transient registered class.
But, if think about it, that makes sense. The settings need to be reloaded when they change so if you inject them into a Singleton then the class will never get the updated settings because the constructor will not be called again for a Singleton.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With