ChangeToken.OnChange不会在自定义配置提供程序上触发

时间:2018-02-13 15:25:26

标签: c# asp.net-core configuration

我将尝试创建自定义cinfig提供程序,该提供程序将从数据库中获取密钥。正如在手册中所写的那样,我创建了这个提供程序并且它的工作正常。所有键都在开始时加载,一切正常。

但是现在我尝试使用IOptionsSnapshot并重新加载来自db的密​​钥。但没有任何反应。

任何人都可以告诉我作品出错了。这是我的代码:

 {"Image":"[object HTMLImageElement]"}---Image was uploaded

我做了什么才开始工作:

  1. 我在类EFConfigProvider变量中添加
  2.     public class EFConfigProvider : ConfigurationProvider
    {
        private DateTime lastLoaded;
        public EFConfigProvider(Action<DbContextOptionsBuilder> optionsAction)
        {
            OptionsAction = optionsAction;
            lastLoaded = DateTime.Now;
            ChangeToken.OnChange(
                () => Watch(),
                () => {
                    Thread.Sleep(250);
                    this.Load();
                });
        }
    
        public new IChangeToken GetReloadToken()
        {
            return Watch();
        }
    
        Action<DbContextOptionsBuilder> OptionsAction { get; }
    
        // Load config data from EF DB.
        public override void Load()
        {
            this.Data.Clear();
    
            var builder = new DbContextOptionsBuilder<ConfigContext>();
            OptionsAction(builder);
    
            using (var dbContext = new ConfigContext(builder.Options))
            {
                // Save Load Fact
                dbContext.SaveLoadFact();
                // Load Partners Settings
                GetPartners(dbContext);
            }
        }
    
        private IChangeToken Watch()
        {
            return new DatabaseChangeToken();
        }
    }
    
    public class DatabaseChangeToken : IChangeToken
    {
        public bool HasChanged
        {
            get
            {
                return true;
            }
        }
        public bool ActiveChangeCallbacks => false;
    
        public IDisposable RegisterChangeCallback(Action<object> callback, object state) => EmptyDisposable.Instance;
    
        internal class EmptyDisposable : IDisposable
        {
            public static EmptyDisposable Instance { get; } = new EmptyDisposable();
            private EmptyDisposable() { }
            public void Dispose() { }
        }
    }
    
    1. 我添加构造函数
    2. private ConfigurationReloadToken _reloadToken = new ConfigurationReloadToken();
      
      1. 我为periodTask添加了课程
      2. // Start Periodic task to refresh the DB
                PeriodicTask.Run(() =>
                {
                    //Refresh();
                    OnReload();
                }, TimeSpan.FromSeconds(reload));
        
        1. 在Reload上添加方法
        2. public class PeriodicTask
          {
              public static async Task Run(Action action, TimeSpan period, CancellationToken cancellationToken)
              {
                  while (!cancellationToken.IsCancellationRequested)
                  {
                      await Task.Delay(period, cancellationToken);
          
                      if (!cancellationToken.IsCancellationRequested)
                          action();
                  }
              }
          
              public static Task Run(Action action, TimeSpan period)
              {
                  return Run(action, period, CancellationToken.None);
              }
          }
          
          1. 添加使用更改令牌的更改
          2. protected new void OnReload()
                    {
                        var previousToken = Interlocked.Exchange(ref _reloadToken, new ConfigurationReloadToken());
                        previousToken.OnReload();
                    }
            

2 个答案:

答案 0 :(得分:0)

已更新:我发现覆盖exec(open("./lib/@file_directory/my_classes.py").read()) 会中断更改通知(我正在使用OnReload(),但未看到更改)。因此,我更改了实现以不这样做。它正在调用默认的OptionsMonitor,该OnReload()处理OptionsMonitor的更改通知。


您更新后的实施确实帮助了我,所以谢谢您!

除了使用自定义PeriodicTask之外,您还可以使用documentation。效果是一样的。

这是我的实现,它每5分钟重新加载一次数据:

using System;
using System.Linq;
using System.Threading;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Primitives;
using Timer = System.Timers.Timer;

namespace MyProject.Classes.Configuration {
    public class MyConfigProvider : ConfigurationProvider {
        private readonly DbContextOptions<MyDbContext> _dbOptions;
        private readonly Timer _reloadTimer = new Timer();
        private ConfigurationReloadToken _reloadToken = new ConfigurationReloadToken();

        public MyConfigProvider(Action<DbContextOptionsBuilder> dbOptionsAction) {
            var builder = new DbContextOptionsBuilder<MyDbContext>();
            dbOptionsAction(builder);
            _dbOptions = builder.Options;

            _reloadTimer.AutoReset = false;
            _reloadTimer.Interval = TimeSpan.FromMinutes(5).TotalMilliseconds;
            _reloadTimer.Elapsed += (s, e) => { Load(); };
        }

        public override void Load() {
            try {
                using (var db = new MyDbContext(_dbOptions)) {
                    var settings = db.Settings.AsNoTracking().ToList();
                    Data.Clear();

                    foreach (var s in settings) {
                        Data.Add(s.Name, s.Value);
                    }
                }
                OnReload();
            } finally {
                _reloadTimer.Start();
            }
        }
    }
}

答案 1 :(得分:-2)

我有相同的要求,我找到了你的代码,我尝试了同样的事情,但没有工作。

但是你的代码和其他一些调查引导我提示。

public class CustomConfigurationProvider : ConfigurationProvider
{
    private readonly string applicationName;
    private readonly bool reloadOnChange;
    private readonly IConfiguration configuration;

    public CustomConfigurationProvider(string applicationName, bool reloadOnChange)
    {
        this.applicationName = applicationName;
        this.reloadOnChange = reloadOnChange;

        if(reloadOnChange)
        {
        ChangeToken.OnChange(
            () => GetReloadToken(), // listener to token change
            () =>
            {
                Thread.Sleep(250);
                this.Load();
            });
        }
    }

    public override async void Load()
    {
        Data.Clear();

        Data = read data from database;

        if (Condition to check if data in database changed)
        {
            OnReload(); // This will create new token and trigger change so what is register in OnChange above will be called again which is this.Load()
        }
    }
}

我也参考了https://www.mikesdotnetting.com/article/301/loading-asp-net-core-mvc-views-from-a-database-or-other-location

https://docs.microsoft.com/en-us/aspnet/core/fundamentals/primitives/change-tokens

希望这有帮助。