最近又研究了一下.NetCore配置选项的源码实现,又学习到了不少东西。这篇文章先写一下IConfiguration的学习成果,Options的后面补上
核心类ConfigurationBuilder:IConfigurationBuilder (构建IConfiguration)
IConfigurationSource (配置数据来源)
IConfigurationProvider (将配置源的原始结构转为为IDictionary<string, string>)
ConfigurationRoot:IConfigurationRoot:IConfiguration (配置根节点)
构建 ConfigurationBuilder下面是ConfigurationBuilder中的主要代码
可以看到ConfigurationBuilder的主要功能就是配置数据源到集合中
在Build时依次调用IConfigurationSource的Build函数,并将返回的IConfigurationProvider加入到List中
最后用IConfigurationProvider的集合构建一个ConfigurationRoot对象
public IList<IConfigurationSource> Sources = new List<IConfigurationSource>(); public IConfigurationBuilder Add(IConfigurationSource source) { Sources.Add(source); return this; } public IConfigurationRoot Build() { List<IConfigurationProvider> list = new List<IConfigurationProvider>(); foreach (IConfigurationSource source in Sources) { IConfigurationProvider item = source.Build(this); list.Add(item); } return new ConfigurationRoot(list); }
IConfigurationSourcepublic class EnvironmentVariablesConfigurationSource : IConfigurationSource { public string Prefix; public IConfigurationProvider Build(IConfigurationBuilder builder) { return new EnvironmentVariablesConfigurationProvider(Prefix); } public EnvironmentVariablesConfigurationSource() { } } public class CommandLineConfigurationSource : IConfigurationSource { public IDictionary<string, string> SwitchMappings; public IEnumerable<string> Args; public IConfigurationProvider Build(IConfigurationBuilder builder) { return new CommandLineConfigurationProvider(Args, SwitchMappings); } public CommandLineConfigurationSource() { } } //JsonConfigurationSource继承自FileConfigurationSource,我这里将其合为一个了 public abstract class JsonConfigurationSource : IConfigurationSource { public IFileProvider FileProvider { get; set; } public string Path { get; set; } public bool Optional { get; set; } public bool ReloadOnChange { get; set; } public int ReloadDelay { get; set; } = 250; public Action<FileLoadExceptionContext> OnLoadException { get; set; } public IConfigurationProvider Build(IConfigurationBuilder builder) { FileProvider = FileProvider ?? builder.GetFileProvider(); OnLoadException = OnLoadException ?? builder.GetFileLoadExceptionHandler(); return new JsonConfigurationProvider(this); } public void ResolveFileProvider() { if (FileProvider == null && !string.IsNullOrEmpty(Path) && System.IO.Path.IsPathRooted(Path)) { string directoryName = System.IO.Path.GetDirectoryName(Path); string text = System.IO.Path.GetFileName(Path); while (!string.IsNullOrEmpty(directoryName) && !Directory.Exists(directoryName)) { text = System.IO.Path.Combine(System.IO.Path.GetFileName(directoryName), text); directoryName = System.IO.Path.GetDirectoryName(directoryName); } if (Directory.Exists(directoryName)) { FileProvider = new PhysicalFileProvider(directoryName); Path = text; } } } }
上面展示了比较常用的三种ConfigurationSource,代码都比较简单。
也很容易看出来ConfigurationSource的作用就是配置数据源,并不解析数据。
解析数据源的功能由 IConfigurationProvider完成
ConfigurationProvider下面为IConfigurationProvider接口定义的5个函数
public interface IConfigurationProvider { bool TryGet(string key, out string value); void Set(string key, string value); IChangeToken GetReloadToken(); void Load(); IEnumerable<string> GetChildKeys(IEnumerable<string> earlierKeys, string parentPath); }
ConfigurationProvider是一个抽象类,继承了IConfigurationProvider接口
在新建Provider时一般都会选择直接继承ConfigurationProvider,接下来看一下ConfigurationProvider的几个核心方法
public abstract class ConfigurationProvider : IConfigurationProvider { private ConfigurationReloadToken _reloadToken = new ConfigurationReloadToken(); protected IDictionary<string, string> Data= new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); public virtual bool TryGet(string key, out string value)=>Data.TryGetValue(key, out value); public virtual void Set(string key, string value)=>Data[key] = value; public virtual void Load(){} public IChangeToken GetReloadToken() { return _reloadToken; } protected void OnReload() { ConfigurationReloadToken configurationReloadToken = Interlocked.Exchange(ref _reloadToken, new ConfigurationReloadToken()); configurationReloadToken.OnReload(); }
可以推测出:
Load函数负责从源数据读取数据然后给字典Data赋值
ConfigurationProvider将数据存储在字典Data中,增加修改都是对字典的操作
每个ConfigurationProvider都会生成一个IChangeToken,在OnReload函数被调用时生成新的Token,并调用原Token的OnReload函数
ConfigurationRoot