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

  

 
 

 

 




 
 

posted on 2024-07-07 19:31  是水饺不是水饺  阅读(51)  评论(0)    收藏  举报

导航