Reputation: 119
I am working on Asp.Net Core app
I want to change the configuration settings after running the application
I am using IOptionsMonitor, but it is not detecting changes
In Startup.cs -> Configuration() method I have
services.Configure<Config>(Configuration.GetSection("someConfig"));
In a different class where these config settings are read, I wrote something like
var someConfig= serviceProvider.GetRequiredService<IOptionsMonitor<Config>>();
But when I change the configuration file (Json File), the change is not detected, and someConfig does not change.
Config POCO class:
public class Config
{
public string name {get; set;}
//More getters and setters
}
Edit:
services.AddSingleton<ConfigHelpers>;
I am using a singleton object in which I am trying to read the config. It works fine if its not a snigleton. Is there a way to change the config even in a singleton object ?
in ConfigHelpers.cs
var someConfig= serviceProvider.GetRequiredService<IOptionsMonitor<Config>();
since it is defined as singleton in Startup.cs, changes made to Config are not reflected.
Upvotes: 6
Views: 8649
Reputation: 31
And for noobs like myself, maybe I can save you a little time by pointing out that you need to update the appsettings.json
in your build directory, not the one in your project directory, to be able to see changes at runtime.
Took me a while to figure out that I was not running into an ASP / .Net issue, but that instead it was a classic PEBCAK.
PS:
I used the (newer) WebApplication.CreateBuilder(args)
that already configures a lot of default stuff. And configured options using the method in the original post:
builder.Services.Configure<Config>(Configuration.GetSection("someConfig"));
I just needed to use an injected IOptionsMonitor<Config>
in my (background) service with no need to include an OnChange
handler. Everything worked fine.
Upvotes: 2
Reputation: 15388
To detect configuration changes, a listener must be registered to the IOptionsMonitor
by using its OnChange
method.
Example on a BackgroundService
:
// ...
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Options;
using System.Threading;
public class MySingletonService : BackgroundService
{
private IDisposable _optionsChangedListener;
private MyOptions _myCurrentOptions;
public MySingletonService(IOptionsMonitor<MyOptions> optionsMonitor)
{
_optionsChangedListener = optionsMonitor.OnChange(MyOptionsChanged);
_myCurrentOptions = optionsMonitor.CurrentValue;
}
private void MyOptionsChanged(MyOptions newOptions, string arg2)
{
_myCurrentOptions = newOptions;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
Console.WriteLine(_myCurrentOptions.MyProperty);
await Task.Delay(1000, stoppingToken);
}
}
public override void Dispose()
{
_optionsChangedListener.Dispose();
base.Dispose();
}
}
public class MyOptions
{
public const string SectionKey = "MyOptions";
public string MyProperty { get; set; }
}
Upvotes: 2
Reputation: 2007
After a long day with .NET Core 3.1 reading the docs, various blog and forum posts and implementing all the suggested variations of IOptionsMonitor, IOptions, IOptionsSnapshot, IConfiguration, IServiceCollection and IServiceProvider, the only way I could get a dynamic update to work with the JsonConfigurationProvider (appsettings.json) and ConfigureWebHostDefaults() was this rather ugly workaround that could be implemented in a singleton as per the question:
/// <summary>
/// Reference to the application IConfiguration implementation
/// </summary>
static IConfiguration configuration;
/// <summary>
/// Reference buffer for Configuration property
/// </summary>
static AppConfiguration appConfiguration = new AppConfiguration();
/// <summary>
/// Access to the current configuration
/// </summary>
public static AppConfiguration Configuration
{
get
{
configuration.Bind("AppConfiguration", appConfiguration);
return appConfiguration;
}
}
Upvotes: 1
Reputation: 3265
Did you create your WebHostBuilder like this aprox:
var config = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("config.json", optional: false, reloadOnChange: true)
.AddEnvironmentVariables()
.Build();
var AppConfig = config.Get<AppConfig>();
var host = new WebHostBuilder()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseUrls("http://*:" + (AppConfig.Port ?? 80))
.UseKestrel()
.UseIISIntegration()
// Clones your config values
.UseConfiguration(config)
.UseStartup<Startup>()
.Build();
Then it will clone your values and you'll loose the live reload ability.
You'll have to use ConfigureAppConfiguration and do the following:
var host = new WebHostBuilder()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseUrls("http://*:" + (AppConfig.Port ?? 80))
.UseKestrel()
.ConfigureAppConfiguration((builder, conf) =>
{
conf.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("config.json", optional: false, reloadOnChange: true)
.AddEnvironmentVariables();
})
.UseIISIntegration()
//.UseConfiguration(config)
.UseStartup<Startup>()
.Build();
Upvotes: 8