Hello @Silent Noise
The OnChange
method being called twice could be due to a variety of reasons. Here are a few possibilities:
File System Events: The file system might be raising multiple events for a single change, such as a write
event followed by a close
event. The OnChange
method might be responding to each of these events.
Multiple Subscriptions: There might be multiple subscriptions to the OnChange
event, causing it to be triggered more than once.
To avoid the OnChange
method being called twice, you could add a condition to check if the configuration has actually changed before calling ConfigChange()
. Here’s a simple example of how you could implement this:
AppSettings previousConfig = null;
var monitor = configurationMonitor.OnChange(config =>
{
if (previousConfig == null || !config.Equals(previousConfig))
{
ConfigChange();
previousConfig = config;
}
});
In this code, ConfigChange()
is only called if the configuration has actually changed. This should prevent OnChange
from being called twice for the same configuration change. Please note that you would need to implement an appropriate Equals
method for the AppSettings
class for this to work correctly.
Remember to test this solution thoroughly to ensure it works as expected in your specific use case. If the problem persists, you might want to consider using a debouncing technique (see below) to limit how often OnChange
can be called.
Debouncing technique
This technique uses a timer to delay the execution of the ConfigChange()
method until a certain amount of time has passed without any new changes. If a new change occurs before the timer expires, the timer is reset.
using System;
using System.Threading;
using System.Threading.Tasks;
public class Program
{
static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);
builder.Configuration.AddXmlFile("appsettings.xml", optional: false, reloadOnChange: true);
builder.Services.Configure<AppSettings>(builder.Configuration.GetSection("Settings"));
var app = builder.Build();
var configurationMonitor = app.Services.GetRequiredService<IOptionsMonitor<AppSettings>>();
var last = 0;
Action<AppSettings> debouncedConfigChange = Debounce<AppSettings>(config => ConfigChange(config), 300);
var monitor = configurationMonitor.OnChange(debouncedConfigChange);
app.Run();
}
static Action<T> Debounce<T>(Action<T> func, int milliseconds = 300)
{
var last = 0;
return arg =>
{
var current = Interlocked.Increment(ref last);
Task.Delay(milliseconds).ContinueWith(task =>
{
if (current == last) func(arg);
task.Dispose();
});
};
}
static void ConfigChange(AppSettings config)
{
// Your code here
}
}
In this code, Debounce<T>
is a method that returns a new function. This new function, when called, starts a timer that waits for the specified debounce time. If the function is called again before the timer expires, the timer is reset. If the timer expires without the function being called again, it calls the original function func
.
Please note that this is a simple implementation and might not cover all edge cases. Always make sure to test thoroughly to ensure it works as expected in your specific use case.
If this answers your question, please tag as answered.