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();
}