Skip to content

Instantly share code, notes, and snippets.

@paulomorgado
Created November 7, 2025 18:49
Show Gist options
  • Select an option

  • Save paulomorgado/53820e5c6c3093864f86444f9a262074 to your computer and use it in GitHub Desktop.

Select an option

Save paulomorgado/53820e5c6c3093864f86444f9a262074 to your computer and use it in GitHub Desktop.
Force reload of IOptionsMonitor
#:sdk Microsoft.NET.Sdk
#:package Microsoft.Extensions.Hosting@10.0.0-rc.2.25502.107
#:package Microsoft.Extensions.Logging@10.0.0-rc.2.25502.107
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.Primitives;
using System;
var builder = Host.CreateApplicationBuilder(args);
// Start with no default logging providers, then add console logging.
builder.Logging.ClearProviders();
builder.Logging.AddConsole();
builder.Configuration["Logging:LogLevel:Default"] = "Trace";
var changeTokenSource = new OptionsChangeTokenSource<LoggerFilterOptions>();
builder.Services.AddSingleton<IOptionsChangeTokenSource<LoggerFilterOptions>>(changeTokenSource);
using var host = builder.Build();
// Start the host so logging and DI are active (optional for one-off logs).
await host.StartAsync();
var options = host.Services.GetRequiredService<IOptionsMonitor<LoggerFilterOptions>>();
var logger = host.Services.GetRequiredService<ILogger<Program>>();
Log(0, logger);
var configuration = host.Services.GetRequiredService<IConfiguration>();
configuration["Logging:LogLevel:Default"] = "Error";
changeTokenSource.Reload();
Log(1, logger);
// Stop and dispose the host cleanly.
await host.StopAsync();
static void Log(int eventId, ILogger logger)
{
foreach (var level in Enum.GetValues<LogLevel>())
{
_ = logger.IsEnabled(level);
logger.Log(level, new EventId(eventId), "This is a log message at level {LogLevel}", level);
}
}
class OptionsChangeTokenSource(string? name = null)
{
private event Action changeTokenCallback;
public string? Name { get; } = name;
public IChangeToken GetChangeToken() => new OptionsChangeToken(this);
public void Reload() => changeTokenCallback?.Invoke();
private class OptionsChangeToken(OptionsChangeTokenSource source) : IChangeToken
{
public bool HasChanged { get; }
public bool ActiveChangeCallbacks { get; }
public IDisposable RegisterChangeCallback(Action<object?> callback, object? state) => new OptionsChangeTokenDisposable(source, callback, state);
private class OptionsChangeTokenDisposable : IDisposable
{
private OptionsChangeTokenSource source;
private Action callback;
public OptionsChangeTokenDisposable(OptionsChangeTokenSource source, Action<object?> callback, object? state)
{
this.source = source;
this.callback = () => callback(state);
this.source.changeTokenCallback += this.callback;
}
public void Dispose()
{
this.source.changeTokenCallback -= this.callback;
}
}
}
}
class OptionsChangeTokenSource<TOptions>(string? name = null) : OptionsChangeTokenSource(name), IOptionsChangeTokenSource<TOptions>
{
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment