ASP.NET Core configuration reloadOnChange with IOp

2020-02-26 07:40发布

问题:

I'm using ASP.NET Core 2.0 and I have configuration code like this in the Main method:

public static void Main(string[] args)
{
    var environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
    var configuration = new ConfigurationBuilder()
        .SetBasePath(Directory.GetCurrentDirectory())
        .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
        .AddJsonFile($"appsettings.{environment ?? "Production"}.json", optional: true, reloadOnChange: true)
        .AddEnvironmentVariables()
        .AddCommandLine(args)
        .Build();
}

I have the reloadOnChange set to true, and in my controller I am using IOptionsSnapshot

public HomeController(ILogger<HomeController> logger, IOptionsSnapshot<AppSettings> options)

But when I modify the values in my appsettings.json, I have to restart my app every time or the changes are not being picked up just by refreshing the browser. What am I doing wrong? I've tried to run the app both with console and IIS Express; I've also tried IOptionsMonitor, same thing. Btw., what is the difference between IOptionsMonitor and IOptionsSnapshot?

回答1:

As mentioned in the documentation, just enabling reloadOnChange and then injecting IOptionsSnapshot<T> instead of IOptions<T> will be enough. That requires you to have properly configured that type T though. Usually a configuration registration will look like this:

services.Configure<AppSettings>(Configuration.GetSection("AppSettings"));

However, looking closer at your code, it does not seem that you are using the new ASP.NET Core 2.0 way of configuring your program. The configuration is now part of dependency injection, so you will set it up as part of the WebHostBuilder, using ConfigureAppConfiguration. That could for example look like this:

public static IWebHost BuildWebHost()
    => new WebHostBuilder()
        .UseKestrel()
        .UseContentRoot(Directory.GetCurrentDirectory())
        .ConfigureAppConfiguration((builderContext, config) =>
        {
            IHostingEnvironment env = builderContext.HostingEnvironment;

            config.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
            config.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);
        })
        .UseStartup<Startup>()
        .Build();

If you are using the default builder using WebHost.CreateDefaultBuilder(), then you don’t even need to do this, as the configuration is then automatically set up like that with reloadOnChange activated.


The difference between IOptionsSnapshot and IOptionsMonitor is that the IOptionsSnapshot will just give you a snapshot of the options at the time the IOptionsSnapshot<T> object is being constructed.

That’s why the usage is exactly the same as with IOptions<T>: You inject it in the constructor and then store the options.Value in the instance to access the options later. At that point, that object is fixed and will never change. It’s just that the IOptionsSnapshot<T> is registered as a scoped dependency instead of a singleton dependency like IOptions<T>, so it gets the chance to get the current configuration values on every request instead of just once.

The IOptionsMonitor<T> however is a singleton service that allows you to retrieve the current configuration value at any given time. So it is especially useful for singleton services that need to get the current configuration whenever they need it. In addition, the options monitor offers a push mechanism to get notified of configuration changes by the configuration sources. That way, you can explicitly handle configuration changes.

Options snapshots are designed to be used for transient or scoped dependencies, so you will be fine just using those most of the time. Only in rare occasions when you have to use a singleton service that also needs to have the most updated configuration, you should have the need to use an options monitor. In those cases, note that just switching from snapshots to a monitor will not be enough. Usually, you will have to handle changed configuration in some way (for example clean up state, clear caches etc.). So you should always think about whether you actually need reloadable configuration for everything or if just restarting the application isn’t a viable alternative.