久居深海 发表于 2023-12-2 05:44:59

自定义Configuration配置源

实现自定义配置源至少需要添加如下成员:

[*]实现IConfigurationSource接口的配置源;
[*]实现IConfigurationProvider接口或虚基类ConfigurationProvider的配置提供程序;
[*]添加配置源的IConfigurationBuilder扩展方法;
如自定义一个TXT文本文件配置源:
添加配置源

配置源负责创建配置提供程序,以及监听文件修改。监听文件修改可以使用FileSystemWatcher,通过监听Changed事件监听配置文件的修改。使用ConfigurationReloadToken作为IChangeToken,当监听到文件修改时调用取消令牌的取消操作,进而通知订阅者文件已更改。
public class TxtConfigurationSource : IConfigurationSource, IDisposable
{
    private FileSystemWatcher? _fileWatcher;
    private ConfigurationReloadToken _reloadToken;

    public TxtConfigurationSource(string path, bool reloadOnChange = true)
    {
      FilePath = path;
      ReloadOnChange = reloadOnChange;
      _fileWatcher = new FileSystemWatcher(Directory.GetCurrentDirectory());
      _fileWatcher.Filter = "*.txt";
      _fileWatcher.EnableRaisingEvents = true;
      _fileWatcher.Changed += _fileWatcher_Changed;

      _reloadToken = new ConfigurationReloadToken();
    }

    private void _fileWatcher_Changed(object sender, FileSystemEventArgs e)
    {
      if (e.FullPath != FilePath)
            return;
      
      if (_reloadToken.HasChanged)
            return;

      // 触发事件
      ConfigurationReloadToken previousToken = Interlocked.Exchange(ref _reloadToken,
            new ConfigurationReloadToken());
      previousToken.OnReload();
    }

    public bool ReloadOnChange { get; set; }

    public string FilePath { get; set; }

    public IConfigurationProvider Build(IConfigurationBuilder builder)
    {
      return new TxtConfigurationProvider(this);
    }

    public IChangeToken GetChangeToken() => _reloadToken;

    public void Dispose() => Dispose(true);

    protected virtual void Dispose(bool disposing)
    {
      _fileWatcher?.Dispose();
    }
}添加配置提供程序

配置提供程序负责加载配置文件,并订阅配置源中的配置修改事件。通过ChangeToken.OnChange()方法进行事件订阅,当监听到文件改变时,重新加载文件:
public class TxtConfigurationProvider : ConfigurationProvider, IDisposable
{
    private readonly IDisposable _changeTokenRegistration;

    public TxtConfigurationProvider(TxtConfigurationSource source)
      :base()
    {
      Source = source ?? throw new ArgumentNullException(nameof(source));

      if (Source.ReloadOnChange)
      {
            _changeTokenRegistration = ChangeToken.OnChange(
                () => Source.GetChangeToken(),
                () =>
                {
                  Thread.Sleep(300);
                  Load();
                });
      }
    }

    public TxtConfigurationSource Source { get; }

    public override void Load()
    {
      if (!File.Exists(Source.FilePath))
            return;
      else
      {
            Data = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
            var lines = File.ReadAllLines(Source.FilePath);
            foreach (var line in lines)
            {
                var array = line.Replace(":", ":").Split(':');
                if (array.Length < 2)
                  continue;

                Data.Add(line.Substring(0, line.LastIndexOf(':')), array.Last());
            }
      }

      OnReload();
    }

    public void Dispose() => Dispose(true);

    protected virtual void Dispose(bool disposing)
    {
      _changeTokenRegistration?.Dispose();
    }
}添加配置源扩展方法

添加IConfigurationBuilder扩展方法,方便将自定义配置源添加到IConfigurationBuilder中:
public static IConfigurationBuilder AddTxtFile(this IConfigurationBuilder builder,
    string path, bool reloadOnChange)
{
    if (builder == null)
      throw new ArgumentNullException(nameof(builder));
    if (string.IsNullOrEmpty(path))
      throw new ArgumentException($"文件不能为空:{nameof(path)}");

    return builder.Add(new TxtConfigurationSource(path, reloadOnChange));
}使用

首先通过扩展方法添加配置源,调用IConfigurationBuilder.Build()方法后即可通过IConfiguration获取配置项。通过ChangeToken.OnChange()方法订阅配置修改事件。
using ConfigurationTest;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Primitives;

IConfigurationBuilder configurationBuilder = new ConfigurationBuilder()
    .AddTxtFile(Path.Combine(Directory.GetCurrentDirectory(), "config.txt"), reloadOnChange: true);

// 通过IConfiguration直接读取指定配置
IConfiguration configuration = configurationBuilder.Build();
Console.WriteLine($"FileProvider:Source:{configuration.GetSection("FileProvider:Source").Value}");
Console.WriteLine($"Provider:{configuration["FileProvider:Provider"]}");
Console.WriteLine();

// 通过绑定选项获取配置
FileProviderOptions fileProviderOptions = new FileProviderOptions();
configuration.GetSection("FileProvider").Bind(fileProviderOptions);
Console.WriteLine($"FileProvider.Source = {fileProviderOptions.Source}");
Console.WriteLine($"FileProvider.Provider = {fileProviderOptions.Provider}");
Console.WriteLine();

// 监听配置修改
var disable = ChangeToken.OnChange(() => configuration.GetReloadToken(),
                                 () =>
                                 {
                                       foreach (var section in configuration.GetChildren())
                                       {
                                           PrintAllConfig(section);
                                       }
                                       
                                       Console.WriteLine();
                                       Console.WriteLine("按“q”退出");
                                 });

Console.WriteLine("按“q”退出");
while (Console.ReadLine() != "q")
{
}

disable.Dispose();

void PrintAllConfig(IConfigurationSection config)
{
    var sections = config.GetChildren();
    if(sections == null || sections.Count() == 0)
      Console.WriteLine($"{config.Key} : {config.Value}");
    else
    {
      foreach (var section in sections)
      {
            PrintAllConfig(section);
      }
    }
}

class FileProviderOptions
{
    public string Source { get; set; }
    public string Provider { get; set; }
}// config.txt
FileProvider:Source:TxtSource
FileProvider:Provider:TxtProvider
来源:https://www.cnblogs.com/louzixl/archive/2023/12/02/17871159.html
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!
页: [1]
查看完整版本: 自定义Configuration配置源