最近又研究了一下.NetCore配置選項(xiàng)的源碼實(shí)現(xiàn),又學(xué)習(xí)到了不少東西。這篇文章先寫(xiě)一下IConfiguration的學(xué)習(xí)成果,Options的后面補(bǔ)上
核心類
- ConfigurationBuilder:IConfigurationBuilder (構(gòu)建IConfiguration)
- IConfigurationSource (配置數(shù)據(jù)來(lái)源)
- IConfigurationProvider (將配置源的原始結(jié)構(gòu)轉(zhuǎn)為為IDictionary<string, string>)
- ConfigurationRoot:IConfigurationRoot:IConfiguration (配置根節(jié)點(diǎn))
構(gòu)建
ConfigurationBuilder
下面是ConfigurationBuilder中的主要代碼
可以看到ConfigurationBuilder的主要功能就是配置數(shù)據(jù)源到集合中
在Build時(shí)依次調(diào)用IConfigurationSource的Build函數(shù),并將返回的IConfigurationProvider加入到List中
最后用IConfigurationProvider的集合構(gòu)建一個(gè)ConfigurationRoot對(duì)象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
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); } |
IConfigurationSource
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
|
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,我這里將其合為一個(gè)了 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,代碼都比較簡(jiǎn)單。
也很容易看出來(lái)ConfigurationSource的作用就是配置數(shù)據(jù)源,并不解析數(shù)據(jù)。
解析數(shù)據(jù)源的功能由 IConfigurationProvider完成
ConfigurationProvider
下面為IConfigurationProvider接口定義的5個(gè)函數(shù)
1
2
3
4
5
6
7
8
9
10
11
12
|
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是一個(gè)抽象類,繼承了IConfigurationProvider接口
在新建Provider時(shí)一般都會(huì)選擇直接繼承ConfigurationProvider,接下來(lái)看一下ConfigurationProvider的幾個(gè)核心方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
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(); } |
可以推測(cè)出:
- Load函數(shù)負(fù)責(zé)從源數(shù)據(jù)讀取數(shù)據(jù)然后給字典Data賦值
- ConfigurationProvider將數(shù)據(jù)存儲(chǔ)在字典Data中,增加修改都是對(duì)字典的操作
- 每個(gè)ConfigurationProvider都會(huì)生成一個(gè)IChangeToken,在OnReload函數(shù)被調(diào)用時(shí)生成新的Token,并調(diào)用原Token的OnReload函數(shù)
ConfigurationRoot
在ConfigurationBuilder的Build函數(shù)中,我們生成了一個(gè)ConfigurationRoot,并給他傳遞了所有的ConfigrationProvider列表,下面我們看看他用我們的Provider都做了啥吧
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
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; |
上面的代碼也對(duì)部分地方進(jìn)行了簡(jiǎn)化。可以看到ConfigurationRoot在生成時(shí)主要就做了兩件事
- 1.調(diào)用Provider的Load函數(shù),這會(huì)給Provider的Data賦值
- 2.讀取Provider的ReloadToken,每個(gè)Provider的Reload事件都會(huì)觸發(fā)ConfigurationRoot自己的ReloadToken的Reload事件
至此配置的數(shù)據(jù)源構(gòu)建這塊就分析完啦!
查詢
常規(guī)的配置查詢有兩種基本方式 :索引器和GetSection(string key)
其余的GetValue等等都是一些擴(kuò)展方法,本篇文章不對(duì)此進(jìn)行展開(kāi)研究
索引器
索引器的查詢執(zhí)行的方式是倒敘查詢所有的Provider,然后調(diào)用Provider的TryGet函數(shù),在查詢時(shí)重名的Key,最后加入的會(huì)生效。
賦值則是依次調(diào)用每個(gè)Provider的Set函數(shù)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
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); } } } |
GetSection
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
|
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會(huì)生成一個(gè)ConfigurationSection對(duì)象
而ConfigurationSection在讀取/設(shè)置值時(shí)實(shí)際上就是對(duì)查詢的Key用:拼接,然后調(diào)用IConfigurationRoot(_root)的賦值或查詢函數(shù)
關(guān)于Configuration的配置和讀取的知識(shí)點(diǎn)大概就是以上這些了,還有更深入的涉及到對(duì)象的綁定這一塊Get<> Bind<> GetChildren()等,比較難讀,要一行一行代碼看,以后有時(shí)間可能再研究一下
最后貼上一個(gè)從數(shù)據(jù)加載配置源并動(dòng)態(tài)更新的小例子
DBConfiguration示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
|
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($ "讀取配置時(shí)間:{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($ "更新數(shù)據(jù)庫(kù)數(shù)據(jù)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); } } |
到此這篇關(guān)于.Net Core配置Configuration具體實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān).Net Core Configuration內(nèi)容請(qǐng)搜索服務(wù)器之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持服務(wù)器之家!
原文鏈接:https://www.cnblogs.com/bluesummer/p/15236098.html