.net core Configuration
ConfigurationBuilder configBuilder = new ConfigurationBuilder();
configBuilder.AddJsonFile(path: "appsettings.json", optional: false, reloadOnChange: true);
configBuilder.AddInMemoryCollection(new KeyValuePair<string, string>[] { new("name", "123") });
IConfigurationRoot root = configBuilder.Build();
configBuilder.AddJsonFile(path: "appsettings.json", optional: false, reloadOnChange: true);
Path方法用于设置配置文件搜索的基础路径。optional: true参数表示如果这个文件不存在,应用程序不应该抛出异常。reloadOnChange:true参数意味着如果文件发生改变,配置将自动重新加载。reloadOnChange=true 文件发生改变为什么能自动重新加载AddJsonFile 在程序集 Microsoft.Extensions.Configuration.Json 中

这个程序集 中 JsonConfigurationFileParser 类 可以将stream 转换 成 字典 这个类有点意思
主要查看这个类 JsonConfigurationProvider Load stream 转换

查看类 FileConfigurationProvider 构造函数 中原来 有ChangeToken.Onchange的 调用 哦原来核心就是这个

把核心代码 拿出来看看
ChangeToken.OnChange(
() => Source.FileProvider.Watch(Source.Path!),
() =>
{
Thread.Sleep(Source.ReloadDelay);
Load(reload: true);
});
ChangeToken.OnChange 就有两个静态方法

继续
Func<IChangeToken?> changeTokenProducer
这个委托 返回 IChangeToken 也就是 这段代码返回的 Source.FileProvider.Watch(Source.Path!)
ChangeToken 源码
调用 OnChange(Func<IChangeToken?> changeTokenProducer, Action changeTokenConsumer)
返回 return new ChangeTokenRegistration<Action>(changeTokenProducer, callback => callback(), changeTokenConsumer);
查看 源码 核心 调用
IDisposable registraton = token.RegisterChangeCallback(s => ((ChangeTokenRegistration<TState>?)s)!.OnChangeTokenFired(), this);
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Threading;
namespace Microsoft.Extensions.Primitives
{
/// <summary>
/// Propagates notifications that a change has occurred.
/// </summary>
public static class ChangeToken
{
/// <summary>
/// Registers the <paramref name="changeTokenConsumer"/> action to be called whenever the token produced changes.
/// </summary>
/// <param name="changeTokenProducer">Produces the change token.</param>
/// <param name="changeTokenConsumer">Action called when the token changes.</param>
/// <returns></returns>
public static IDisposable OnChange(Func<IChangeToken?> changeTokenProducer, Action changeTokenConsumer)
{
if (changeTokenProducer is null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.changeTokenProducer);
}
if (changeTokenConsumer is null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.changeTokenConsumer);
}
return new ChangeTokenRegistration<Action>(changeTokenProducer, callback => callback(), changeTokenConsumer);
}
/// <summary>
/// Registers the <paramref name="changeTokenConsumer"/> action to be called whenever the token produced changes.
/// </summary>
/// <param name="changeTokenProducer">Produces the change token.</param>
/// <param name="changeTokenConsumer">Action called when the token changes.</param>
/// <param name="state">state for the consumer.</param>
/// <returns></returns>
public static IDisposable OnChange<TState>(Func<IChangeToken?> changeTokenProducer, Action<TState> changeTokenConsumer, TState state)
{
if (changeTokenProducer is null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.changeTokenProducer);
}
if (changeTokenConsumer is null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.changeTokenConsumer);
}
return new ChangeTokenRegistration<TState>(changeTokenProducer, changeTokenConsumer, state);
}
private sealed class ChangeTokenRegistration<TState> : IDisposable
{
private readonly Func<IChangeToken?> _changeTokenProducer;
private readonly Action<TState> _changeTokenConsumer;
private readonly TState _state;
private IDisposable? _disposable;
private static readonly NoopDisposable _disposedSentinel = new NoopDisposable();
public ChangeTokenRegistration(Func<IChangeToken?> changeTokenProducer, Action<TState> changeTokenConsumer, TState state)
{
_changeTokenProducer = changeTokenProducer;
_changeTokenConsumer = changeTokenConsumer;
_state = state;
IChangeToken? token = changeTokenProducer();
RegisterChangeTokenCallback(token);
}
private void OnChangeTokenFired()
{
// The order here is important. We need to take the token and then apply our changes BEFORE
// registering. This prevents us from possible having two change updates to process concurrently.
//
// If the token changes after we take the token, then we'll process the update immediately upon
// registering the callback.
IChangeToken? token = _changeTokenProducer();
try
{
_changeTokenConsumer(_state);
}
finally
{
// We always want to ensure the callback is registered
RegisterChangeTokenCallback(token);
}
}
private void RegisterChangeTokenCallback(IChangeToken? token)
{
if (token is null)
{
return;
}
IDisposable registraton = token.RegisterChangeCallback(s => ((ChangeTokenRegistration<TState>?)s)!.OnChangeTokenFired(), this);
if (token.HasChanged && token.ActiveChangeCallbacks)
{
registraton?.Dispose();
return;
}
SetDisposable(registraton);
}
private void SetDisposable(IDisposable disposable)
{
// We don't want to transition from _disposedSentinel => anything since it's terminal
// but we want to allow going from previously assigned disposable, to another
// disposable.
IDisposable? current = Volatile.Read(ref _disposable);
// If Dispose was called, then immediately dispose the disposable
if (current == _disposedSentinel)
{
disposable.Dispose();
return;
}
// Otherwise, try to update the disposable
IDisposable? previous = Interlocked.CompareExchange(ref _disposable, disposable, current);
if (previous == _disposedSentinel)
{
// The subscription was disposed so we dispose immediately and return
disposable.Dispose();
}
else if (previous == current)
{
// We successfully assigned the _disposable field to disposable
}
else
{
// Sets can never overlap with other SetDisposable calls so we should never get into this situation
throw new InvalidOperationException("Somebody else set the _disposable field");
}
}
public void Dispose()
{
// If the previous value is disposable then dispose it, otherwise,
// now we've set the disposed sentinel
Interlocked.Exchange(ref _disposable, _disposedSentinel)?.Dispose();
}
private sealed class NoopDisposable : IDisposable
{
public void Dispose()
{
}
}
}
}
}
经过上面的源码查看 IDisposable registraton = token.RegisterChangeCallback(s => ((ChangeTokenRegistration<TState>?)s)!.OnChangeTokenFired(), this);
token.RegisterChangeCallback 这段代码 是核心 token 是 IChangeToken 中的 RegisterChangeCallback 回调方法
该方法是 token 改变的回调方法 如果 token调用 cancel 那么会触发这个方法
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
namespace Microsoft.Extensions.Primitives
{
/// <summary>
/// Propagates notifications that a change has occurred.
/// </summary>
public interface IChangeToken
{
/// <summary>
/// Gets a value that indicates if a change has occurred.
/// </summary>
bool HasChanged { get; }
/// <summary>
/// Indicates if this token will pro-actively raise callbacks. If <c>false</c>, the token consumer must
/// poll <see cref="HasChanged" /> to detect changes.
/// </summary>
bool ActiveChangeCallbacks { get; }
/// <summary>
/// Registers for a callback that will be invoked when the entry has changed.
/// <see cref="HasChanged"/> MUST be set before the callback is invoked.
/// </summary>
/// <param name="callback">The <see cref="Action{Object}"/> to invoke.</param>
/// <param name="state">State to be passed into the callback.</param>
/// <returns>An <see cref="IDisposable"/> that is used to unregister the callback.</returns>
IDisposable RegisterChangeCallback(Action<object?> callback, object? state);
}
}
经过源码查看 大致了解了 原理 重回 最上面的源码
ChangeToken.OnChange(
() => Source.FileProvider.Watch(Source.Path!),
() =>
{
Thread.Sleep(Source.ReloadDelay);
Load(reload: true);
});
Source.FileProvider.Watch(Source.Path!) 生成变化后的token 通过token的 RegisterChangeCallback 方法 调用 下面的委托 实现了 重新加载
{
Thread.Sleep(Source.ReloadDelay);
Load(reload: true);
}
load源码
private void Load(bool reload)
{
IFileInfo? file = Source.FileProvider?.GetFileInfo(Source.Path ?? string.Empty);
if (file == null || !file.Exists)
{
if (Source.Optional || reload) // Always optional on reload
{
Data = new Dictionary<string, string?>(StringComparer.OrdinalIgnoreCase);
}
else
{
var error = new StringBuilder(SR.Format(SR.Error_FileNotFound, Source.Path));
if (!string.IsNullOrEmpty(file?.PhysicalPath))
{
error.Append(SR.Format(SR.Error_ExpectedPhysicalPath, file.PhysicalPath));
}
HandleException(ExceptionDispatchInfo.Capture(new FileNotFoundException(error.ToString())));
}
}
else
{
static Stream OpenRead(IFileInfo fileInfo)
{
if (fileInfo.PhysicalPath != null)
{
// The default physical file info assumes asynchronous IO which results in unnecessary overhead
// especially since the configuration system is synchronous. This uses the same settings
// and disables async IO.
return new FileStream(
fileInfo.PhysicalPath,
FileMode.Open,
FileAccess.Read,
FileShare.ReadWrite,
bufferSize: 1,
FileOptions.SequentialScan);
}
return fileInfo.CreateReadStream();
}
using Stream stream = OpenRead(file);
try
{
Load(stream);
}
catch (Exception ex)
{
if (reload)
{
Data = new Dictionary<string, string?>(StringComparer.OrdinalIgnoreCase);
}
var exception = new InvalidDataException(SR.Format(SR.Error_FailedToLoad, file.PhysicalPath), ex);
HandleException(ExceptionDispatchInfo.Capture(exception));
}
}
// REVIEW: Should we raise this in the base as well / instead?
OnReload();
}
/// <summary>
/// Loads this provider's data from a stream.
/// </summary>
/// <param name="stream">The stream to read.</param>
public abstract void Load(Stream stream);
这个是抽象方法 JsonConfigurationProvider 重新了父类 Load 方法 完成了 重新加载

Source.FileProvider.Watch(Source.Path!)
生成变化后的token 通过token的 RegisterChangeCallback 方法 调用 下面的委托 实现了 重新加载
{Thread.Sleep(Source.ReloadDelay); Load(reload: true); } Load 被 JsonConfigurationProvider 重写了 加载了
Data = JsonConfigurationFileParser.Parse(stream);
这就是原理
其实 核心 就是接口 IChangeToken 这个接口的实现 是类 CancellationChangeToken 下面是源码
using System;
using System.Diagnostics;
using System.Threading;
using Microsoft.Extensions.Internal;
namespace Microsoft.Extensions.Primitives
{
/// <summary>
/// A <see cref="IChangeToken"/> implementation using <see cref="CancellationToken"/>.
/// </summary>
[DebuggerDisplay("HasChanged = {HasChanged}")]
public class CancellationChangeToken : IChangeToken
{
/// <summary>
/// Initializes a new instance of <see cref="CancellationChangeToken"/>.
/// </summary>
/// <param name="cancellationToken">The <see cref="CancellationToken"/>.</param>
public CancellationChangeToken(CancellationToken cancellationToken)
{
Token = cancellationToken;
}
/// <inheritdoc />
public bool ActiveChangeCallbacks { get; private set; } = true;
/// <inheritdoc />
public bool HasChanged => Token.IsCancellationRequested;
private CancellationToken Token { get; }
/// <inheritdoc />
public IDisposable RegisterChangeCallback(Action<object?> callback, object? state)
{
return ChangeCallbackRegistrar.UnsafeRegisterChangeCallback(
callback,
state,
Token,
static s => s.ActiveChangeCallbacks = false, // Reset the flag to indicate to future callers that this wouldn't work.
this);
}
}
}
浙公网安备 33010602011771号