Manually trigger IOptionsMonitor<>.OnChange

前端 未结 1 588
悲&欢浪女
悲&欢浪女 2021-01-26 17:27

In ASP.NET Core 2.1, I use IOptionsMonitor<> and have it set up so I can successfully get events for when I change the appSettings.json file. So this is working.

Wha

相关标签:
1条回答
  • 2021-01-26 17:33

    For IOptionsMonitor<Locations>, it only changes the value in memory and did not save back to appsettings.json. For a workaround, you will need to implement your own method to save the changes back to appsettings.json.

    • define IWritableOptions which inherits from IOptions

      public interface IWritableOptions<out T> : IOptions<T> where T : class, new()
      {
           void Update(Action<T> applyChanges);
      }
      
    • implement your own WritableOptions

      public class WritableOptions<T> : IWritableOptions<T> where T : class, new()
      {
      private readonly IHostingEnvironment _environment;
      private readonly IOptionsMonitor<T> _options;
      private readonly IConfigurationRoot _configuration;
      private readonly string _section;
      private readonly string _file;
      
      public WritableOptions(
          IHostingEnvironment environment,
          IOptionsMonitor<T> options,
          IConfigurationRoot configuration,
          string section,
          string file)
      {
          _environment = environment;
          _options = options;
          _configuration = configuration;
          _section = section;
          _file = file;
      }
      
      public T Value => _options.CurrentValue;
      public T Get(string name) => _options.Get(name);
      
      public void Update(Action<T> applyChanges)
      {
          var fileProvider = _environment.ContentRootFileProvider;
          var fileInfo = fileProvider.GetFileInfo(_file);
          var physicalPath = fileInfo.PhysicalPath;
      
          var jObject = JsonConvert.DeserializeObject<JObject>(File.ReadAllText(physicalPath));
          var sectionObject = jObject.TryGetValue(_section, out JToken section) ?
              JsonConvert.DeserializeObject<T>(section.ToString()) : (Value ?? new T());
      
          applyChanges(sectionObject);
      
          jObject[_section] = JObject.Parse(JsonConvert.SerializeObject(sectionObject));
          File.WriteAllText(physicalPath, JsonConvert.SerializeObject(jObject, Formatting.Indented));
          _configuration.Reload();
      }
      }
      
    • Configure IWritableOptions<T>

      public static class ServiceCollectionExtensions
      {
      public static void ConfigureWritable<T>(
          this IServiceCollection services,
          IConfigurationSection section,
          string file = "appsettings.json") where T : class, new()
      {
          services.Configure<T>(section);
          services.AddTransient<IWritableOptions<T>>(provider =>
          {
              var configuration = (IConfigurationRoot)provider.GetService<IConfiguration>();
              var environment = provider.GetService<IHostingEnvironment>();
              var options = provider.GetService<IOptionsMonitor<T>>();
              return new WritableOptions<T>(environment, options, configuration, section.Key, file);
          });
      }
      }
      
    • Register in Startup

              services.ConfigureWritable<Locations>(Configuration.GetSection("Locations"));
      
    • Use

      public class OptionsController : Controller
      {
      private readonly IWritableOptions<Locations> _writableLocations;
      public OptionsController(IWritableOptions<Locations> writableLocations)
      {
          _writableLocations = writableLocations;
      }
      
      public IActionResult Change(string value)
      {
          _writableLocations.Update(opt => {
              opt.Name = value;
          });
          return Ok("OK");
      }
      }
      
    • It will fire the IOptionsMonitor<>.OnChange

    0 讨论(0)
提交回复
热议问题