Configuration

ConfigurationRoot

public interface IConfiguration
{
    string? this[string key] { get; }
    IChangeToken GetReloadToken();
}

public interface IConfigurationRoot : IConfiguration
{
    void Reload();
}

public class ConfigurationRoot : IConfigurationRoot
{
    private readonly List<IConfigurationProvider> _providers;
    private readonly IList<IDisposable> _changeTokenRegistrations;
    private ConfigurationReloadToken _changeToken = new ConfigurationReloadToken();

    public ConfigurationRoot(List<IConfigurationProvider> providers)
    {
        this._providers = providers;
        this._changeTokenRegistrations = new List<IDisposable>(providers.Count);
        foreach (IConfigurationProvider p in providers)
        {
            p.Load();
            _changeTokenRegistrations.Add(ChangeToken.OnChange(() => p.GetReloadToken(), () => RaiseChanged()));
        }
    }

    public string? this[string key]
    {
        get
        {
            return GetConfiguration(_providers, key);
        }
    }

    internal static string? GetConfiguration(List<IConfigurationProvider> providers, string key)
    {
        for (var i = providers.Count - 1; i >= 0; i--)
        {
            var provider = providers[i];
            if (provider.TryGet(key, out var value))
            {
                return value;
            }
        }

        return null;
    }

    public IChangeToken GetReloadToken()
    {
        return _changeToken;
    }

    public void Reload()
    {
        foreach (IConfigurationProvider provider in _providers)
        {
            provider.Load();
        }
        RaiseChanged();
    }

    private void RaiseChanged()
    {
        ConfigurationReloadToken previousToken = Interlocked.Exchange(ref _changeToken, new ConfigurationReloadToken());
        previousToken.OnReload();
    }

}

ConfigurationReloadToken

public class ConfigurationReloadToken : IChangeToken
{
    private CancellationTokenSource _cts = new CancellationTokenSource();

    public bool HasChanged => _cts.IsCancellationRequested;

    public bool ActiveChangeCallbacks => true;

    public IDisposable RegisterChangeCallback(Action<object?> callback, object? state) => _cts.Token.Register(callback, state);

    public void OnReload() => _cts.Cancel();
}

ConfigurationSource

public interface IConfigurationSource
{
    IConfigurationProvider Build(IConfigurationBuilder builder);
}

ConfigurationProvider

public interface IConfigurationProvider
{
    bool TryGet(string key, out string? value);

    void Load();

    IChangeToken GetReloadToken();
}

public abstract class ConfigurationProvider : IConfigurationProvider
{
    private ConfigurationReloadToken _reloadToken = new ConfigurationReloadToken();

    protected ConfigurationProvider()
    {
        Data = new Dictionary<string, string?>(StringComparer.InvariantCultureIgnoreCase);
    }

    protected Dictionary<string, string?> Data { get; set; }

    public virtual bool TryGet(string key, out string? value)
    {
        return Data.TryGetValue(key, out value);
    }

    public virtual void Load()
    {

    }

    public IChangeToken GetReloadToken()
    {
        return _reloadToken;
    }

    protected void OnReload()
    {
        ConfigurationReloadToken previousToken = Interlocked.Exchange(ref _reloadToken, new ConfigurationReloadToken());
        previousToken.OnReload();
    }
}

ConfigurationBuilder

public interface IConfigurationBuilder
{
    IConfigurationRoot Build();
}

public class ConfigurationBuilder : IConfigurationBuilder
{
    public IList<IConfigurationSource> Sources { get; } = new List<IConfigurationSource>();

    public IConfigurationBuilder Add(IConfigurationSource source)
    {
        Sources.Add(source);
        return this;
    }

    public IConfigurationRoot Build()
    {
        var providers = new List<IConfigurationProvider>();

        foreach (var source in Sources)
        {
            providers.Add(source.Build(this));
        }

        return new ConfigurationRoot(providers);
    }
}

示例

public class DemoConfigurationSource : IConfigurationSource
{
    public string Path;

    public DemoConfigurationSource(string path)
    {
        Path = path;
    }

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

    public Dictionary<string, string> LoadData()
    {
        return File.ReadAllLines(Path)
            .Where(x => !string.IsNullOrWhiteSpace(x) && x.Contains(":"))
            .ToDictionary(x => x.Split(":")[0], x => x.Split(":")[1]);
    }
}

public class DemoConfigurationProvider : ConfigurationProvider
{
    private DemoConfigurationSource _source { get; }

    public DemoConfigurationProvider(DemoConfigurationSource source)
    {
        _source = source;

        var dir = Path.GetDirectoryName(source.Path);
        var fileName = Path.GetFileName(source.Path);
        var provider = new PhysicalFileProvider(dir);

        ChangeToken.OnChange(
            () => provider.Watch(fileName),
            () =>
            {
                Thread.Sleep(5000);
                Load();
                // 通知外部有变动
                OnReload();
            });
    }

    public override void Load()
    {
        Data = _source.LoadData();
    }

    private Dictionary<string, string> LoadData()
    {
        return File.ReadAllLines(_source.Path)
            .Where(x => !string.IsNullOrWhiteSpace(x) && x.Contains(":"))
            .ToDictionary(x => x.Split(":")[0], x => x.Split(":")[1]);
    }
}

public static class DemoConfigurationExtensions
{
    public static IConfigurationBuilder AddDemo(this ConfigurationBuilder builder, string path)
    {
        builder.Add(new DemoConfigurationSource(path));
        return builder;
    }
}

static void Main(string[] args)
{
    //var serviceCollection = new ServiceCollection();

    //var service = serviceCollection.BuildServiceProvider();

    var configurationBuilder = new ConfigurationBuilder();
    configurationBuilder.AddDemo("d://1.txt");

    var configuration = configurationBuilder.Build();

    ChangeToken.OnChange(() => configuration.GetReloadToken(), () =>
    {
        var value = configuration["a1"];
        Console.WriteLine($"a1 = {value}");
    });

    //// 手动触发加载
    //configuration.Reload();

    Console.ReadLine();
}
posted @ 2024-09-11 17:56  pojianbing  阅读(25)  评论(0编辑  收藏  举报