您好,登錄后才能下訂單哦!
這篇文章主要介紹“.Net Core怎么配置Configuration”,在日常操作中,相信很多人在.Net Core怎么配置Configuration問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”.Net Core怎么配置Configuration”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!
核心類
構建
ConfigurationBuilder
IConfigurationSource
ConfigurationProvider
ConfigurationRoot
查詢
索引器
GetSection
DBConfiguration示例
最近又研究了一下.NetCore配置選項的源碼實現,又學習到了不少東西。這篇文章先寫一下IConfiguration的學習成果,Options的后面補上
ConfigurationBuilder:IConfigurationBuilder (構建IConfiguration)
IConfigurationSource (配置數據來源)
IConfigurationProvider (將配置源的原始結構轉為為IDictionary<string, string>)
ConfigurationRoot:IConfigurationRoot:IConfiguration (配置根節點)
下面是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); }
public 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完成
下面為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函數
在ConfigurationBuilder的Build函數中,我們生成了一個ConfigurationRoot,并給他傳遞了所有的ConfigrationProvider列表,下面我們看看他用我們的Provider都做了啥吧
private ConfigurationReloadToken _changeToken = new ConfigurationReloadToken(); public ConfigurationRoot(IList<IConfigurationProvider> providers) { _providers = providers; _changeTokenRegistrations = new List<IDisposable>(providers.Count); foreach (IConfigurationProvider p in providers) { p.Load(); ChangeToken.OnChange(p.GetReloadToken, delegate{ var oldToken=Interlocked.Exchange(ref _changeToken, new ConfigurationReloadToken()); oldToken.OnReload(); }) } } public IChangeToken GetReloadToken()=>_changeToken;
上面的代碼也對部分地方進行了簡化。可以看到ConfigurationRoot在生成時主要就做了兩件事
1.調用Provider的Load函數,這會給Provider的Data賦值
2.讀取Provider的ReloadToken,每個Provider的Reload事件都會觸發ConfigurationRoot自己的ReloadToken的Reload事件
至此配置的數據源構建這塊就分析完啦!
常規的配置查詢有兩種基本方式 :索引器和GetSection(string key)
其余的GetValue等等都是一些擴展方法,本篇文章不對此進行展開研究
索引器的查詢執行的方式是倒敘查詢所有的Provider,然后調用Provider的TryGet函數,在查詢時重名的Key,最后加入的會生效。
賦值則是依次調用每個Provider的Set函數
public string this[string key] { get { for (int num = _providers.Count - 1; num >= 0; num--) { if (_providers[num].TryGet(key, out var value)) { return value; } } return null; } set { foreach (IConfigurationProvider provider in _providers) { provider.Set(key, value); } } }
public IConfigurationSection GetSection(string key) { return new ConfigurationSection(this, key); } public class ConfigurationSection : IConfigurationSection, IConfiguration { private readonly IConfigurationRoot _root; private readonly string _path; private string _key; public string Value { get { return _root[Path]; } set { _root[Path] = value; } } //ConfigurationPath.Combine = string.Join(":",paramList); public string this[string key] { get { return _root[ConfigurationPath.Combine(Path, key)]; } set { _root[ConfigurationPath.Combine(Path, key)] = value; } } public ConfigurationSection(IConfigurationRoot root, string path) { _root = root; _path = path; } public IConfigurationSection GetSection(string key) { return _root.GetSection(ConfigurationPath.Combine(Path, key)); } public IEnumerable<IConfigurationSection> GetChildren() { return _root.GetChildrenImplementation(Path); } public IChangeToken GetReloadToken() { return _root.GetReloadToken(); } }
可以看到GetSection會生成一個ConfigurationSection對象
而ConfigurationSection在讀取/設置值時實際上就是對查詢的Key用:拼接,然后調用IConfigurationRoot(_root)的賦值或查詢函數
關于Configuration的配置和讀取的知識點大概就是以上這些了,還有更深入的涉及到對象的綁定這一塊Get<> Bind<> GetChildren()等,比較難讀,要一行一行代碼看,以后有時間可能再研究一下
最后貼上一個從數據加載配置源并動態更新的小例子
public void Run() { var builder = new ConfigurationBuilder(); var dataProvider = new DBDataProvider(); builder.Sources.Add(new DBConfigurationSource() { DataProvider = dataProvider, ReloadOnChange = true, Table = "config" }); IConfigurationRoot config = builder.Build(); Console.WriteLine(config["time"]); Task.Run(() => { while (true) { Thread.Sleep(2000); dataProvider.Update("config"); Console.WriteLine($"讀取配置時間:{config["time"]}"); } }); Thread.Sleep(20000); } public class DBConfigurationProvider : ConfigurationProvider { private DBConfigurationSource Source { get; } public DBConfigurationProvider(DBConfigurationSource source) { Source = source; } public override void Load() { if (Source.ReloadOnChange) { ChangeToken.OnChange(() => Source.DataProvider.Watch(Source.Table), LoadData); } LoadData(); } private void LoadData() { var data = Source.DataProvider.GetData(Source.Table); Load(data); OnReload(); } public void Load(Dictionary<string, object> data) { var dic = new SortedDictionary<string, string>(StringComparer.OrdinalIgnoreCase); foreach (var element in data) { dic.Add(element.Key, element.Value?.ToString()); } base.Data = dic; } } public class DBConfigurationSource : IConfigurationSource { public DBDataProvider DataProvider { get; set; } public string Table { get; set; } public bool ReloadOnChange { get; set; } public bool Optional { get; set; } public DBConfigurationSource() { } public IConfigurationProvider Build(IConfigurationBuilder builder) { return new DBConfigurationProvider(this); } } public class DBDataProvider { private ConcurrentDictionary<string, CancellationTokenSource> tableToken = new ConcurrentDictionary<string, CancellationTokenSource>(); public DBDataProvider() { } public Dictionary<string, object> GetData(string table) { switch (table) { case "config": return GetConfig(); } return new Dictionary<string, object>(); } public void Update(string table) { Console.WriteLine($"更新數據庫數據table:{table}"); if (tableToken.TryGetValue(table, out CancellationTokenSource cts)) { var oldCts = cts; tableToken[table] = new CancellationTokenSource(); oldCts.Cancel(); } } private Dictionary<string, object> GetConfig() { var valueDic = new Dictionary<string, object>(); valueDic.TryAdd("time", DateTime.Now.ToString()); valueDic.TryAdd("weather", "windy"); valueDic.TryAdd("people_number:male", 100); valueDic.TryAdd("people_number:female", 150); return valueDic; } public IChangeToken Watch(string table) { var cts = tableToken.GetOrAdd(table, x => new CancellationTokenSource()); return new CancellationChangeToken(cts.Token); } }
到此,關于“.Net Core怎么配置Configuration”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。