概述
我們都知道asp.net core配置資訊的讀取離不開IConfigurationSource和IConfigurationProvider這兩個類,ConfigurationSource可以提供一個ConfigurationProvider,然后去讀取資訊,究竟他們之間有著怎樣的千絲萬縷,我們一起來看看原始碼,
首先我們來建立一個.net core控制臺專案,來運行以下代碼:
class Program { static void Main(string[] args) { ConfigurationBuilder configBuilder = new ConfigurationBuilder(); configBuilder.SetBasePath(Directory.GetCurrentDirectory()) .AddJsonFile("appsettings.json"); var configFile = configBuilder.Build(); Console.ReadKey(); } }
短短幾行 代碼看起來很簡單,就是用來讀取appsettings.json檔案中的配置資訊,然而我們今天想搞清楚其背后運行的原理,要花點時間,
首先、我們根據代碼ConfigurationBuilder configBuilder = new ConfigurationBuilder();知道創建了一個configBuilder物件;
其次,configBuilder.SetBasePath(Directory.GetCurrentDirectory()) 該代碼的呼叫我們也能大概見名知義,獲取當前的目錄;
接下來,重點來了,configBuilder.AddJsonFile("appsettings.json")的實作究竟是怎樣的?我們來看下原始碼的實作:
f12進去后原始碼如下:
/// <summary>Extension methods for adding <see cref="T:Microsoft.Extensions.Configuration.Json.JsonConfigurationProvider" />.</summary> public static class JsonConfigurationExtensions { /// <summary>Adds the JSON configuration provider at <paramref name="path" /> to <paramref name="builder" />.</summary> /// <param name="builder">The <see cref="T:Microsoft.Extensions.Configuration.IConfigurationBuilder" /> to add to.</param> /// <param name="path">Path relative to the base path stored in /// <see cref="P:Microsoft.Extensions.Configuration.IConfigurationBuilder.Properties" /> of <paramref name="builder" />.</param> /// <returns>The <see cref="T:Microsoft.Extensions.Configuration.IConfigurationBuilder" />.</returns> public static IConfigurationBuilder AddJsonFile( this IConfigurationBuilder builder, string path) { return builder.AddJsonFile((IFileProvider) null, path, false, false); } }
緊接著f12再看實作的原始碼,依然在JsonConfigurationExtensions這個擴展類里面:
public static IConfigurationBuilder AddJsonFile( this IConfigurationBuilder builder, IFileProvider provider, string path, bool optional, bool reloadOnChange) { if (builder == null) throw new ArgumentNullException(nameof (builder)); if (string.IsNullOrEmpty(path)) throw new ArgumentException(SR.Error_InvalidFilePath, nameof (path)); return builder.AddJsonFile((Action<JsonConfigurationSource>) (s => { s.FileProvider = provider; s.Path = path; s.Optional = optional; s.ReloadOnChange = reloadOnChange; s.ResolveFileProvider(); })); }
這時候有沒有發現builder.AddJsonFile((Action<JsonConfigurationSource>)這個方法里面出現了一個關鍵的資訊點:JsonConfigurationSource (JsonConfigurationSource 繼承抽象類FileConfigurationSource,而FileConfigurationSource:IConfigurationSource) , 關系如下圖:


看完上面這個關系圖后,我們緊接著上面builder.AddJsonFile()的實作原始碼繼續f12往下,原始碼如下:
//該代碼依然在JsonConfigurationExtensions類里面
public static IConfigurationBuilder AddJsonFile( this IConfigurationBuilder builder, Action<JsonConfigurationSource> configureSource) { return ConfigurationExtensions.Add<JsonConfigurationSource>(builder, (Action<M0>) configureSource); }
我們看到上面的擴展方法實作是ConfigurationExtensions.Add...,再往下看實作:
public static class ConfigurationExtensions { /// <summary>Adds a new configuration source.</summary> /// <param name="builder">The <see cref="T:Microsoft.Extensions.Configuration.IConfigurationBuilder" /> to add to.</param> /// <param name="configureSource">Configures the source secrets.</param> /// <typeparam name="TSource" /> /// <returns>The <see cref="T:Microsoft.Extensions.Configuration.IConfigurationBuilder" />.</returns> public static IConfigurationBuilder Add<TSource>( this IConfigurationBuilder builder, Action<TSource> configureSource) where TSource : IConfigurationSource, new() { TSource source = new TSource(); if (configureSource != null) configureSource(source); return builder.Add((IConfigurationSource) source); } }
到這里我們看到了其實就是IConfigurationBuilder呼叫了Add方法,添加了一個資料源(JsonConfigurationSource),至于JsonConfigurationSource類里面做了什么,我們看下實作,
public class JsonConfigurationSource : FileConfigurationSource { /// <summary>Builds the <see cref="T:Microsoft.Extensions.Configuration.Json.JsonConfigurationProvider" /> for this source.</summary> /// <param name="builder">The <see cref="T:Microsoft.Extensions.Configuration.IConfigurationBuilder" />.</param> /// <returns>A <see cref="T:Microsoft.Extensions.Configuration.Json.JsonConfigurationProvider" /></returns> public override IConfigurationProvider Build(IConfigurationBuilder builder) { this.EnsureDefaults(builder); return (IConfigurationProvider) new JsonConfigurationProvider(this); } }
JsonConfigurationSource類面的Build方法提供了一個JsonConfigurationProvider類,這里再貼下JsonConfigurationProvider類里面的代碼:
/// <summary>A JSON file based <see cref="T:Microsoft.Extensions.Configuration.FileConfigurationProvider" />.</summary> public class JsonConfigurationProvider : FileConfigurationProvider { /// <summary>Initializes a new instance with the specified source.</summary> /// <param name="source">The source settings.</param> public JsonConfigurationProvider(JsonConfigurationSource source) : base((FileConfigurationSource) source) { } /// <summary>Loads the JSON data from a stream.</summary> /// <param name="stream">The stream to read.</param> public virtual void Load(Stream stream) { try { this.set_Data(JsonConfigurationFileParser.Parse(stream)); } catch (JsonException ex) { throw new FormatException(SR.Error_JSONParseError, (Exception) ex); } } }
關于JsonConfigurationProvider里面的Load就是去讀取資訊的實作,至于Load的具體實作我們不再深究,我們回到最初的控制臺configBuilder.Build(),看看其的實作:
public class ConfigurationBuilder : IConfigurationBuilder { /// <summary>Returns the sources used to obtain configuration values.</summary> public IList<IConfigurationSource> Sources { get; } = (IList<IConfigurationSource>) new List<IConfigurationSource>(); /// <summary>Gets a key/value collection that can be used to share data between the <see cref="T:Microsoft.Extensions.Configuration.IConfigurationBuilder" /> /// and the registered <see cref="T:Microsoft.Extensions.Configuration.IConfigurationProvider" />s.</summary> public IDictionary<string, object> Properties { get; } = (IDictionary<string, object>) new Dictionary<string, object>(); /// <summary>Adds a new configuration source.</summary> /// <param name="source">The configuration source to add.</param> /// <returns>The same <see cref="T:Microsoft.Extensions.Configuration.IConfigurationBuilder" />.</returns> public IConfigurationBuilder Add(IConfigurationSource source) { if (source == null) throw new ArgumentNullException(nameof (source)); this.Sources.Add(source); return (IConfigurationBuilder) this; } /// <summary>Builds an <see cref="T:Microsoft.Extensions.Configuration.IConfiguration" /> with keys and values from the set of providers registered in /// <see cref="P:Microsoft.Extensions.Configuration.ConfigurationBuilder.Sources" />.</summary> /// <returns>An <see cref="T:Microsoft.Extensions.Configuration.IConfigurationRoot" /> with keys and values from the registered providers.</returns> public IConfigurationRoot Build() { List<IConfigurationProvider> configurationProviderList = new List<IConfigurationProvider>(); foreach (IConfigurationSource source in (IEnumerable<IConfigurationSource>) this.Sources) { IConfigurationProvider configurationProvider = source.Build((IConfigurationBuilder) this); configurationProviderList.Add(configurationProvider); } return (IConfigurationRoot) new ConfigurationRoot((IList<IConfigurationProvider>) configurationProviderList); } }
看到這個原始碼的時候有沒有種豁然開朗的感覺,前面我們說到IConfigurationBuilder呼叫了Add方法添加一個資料源,并沒說添加了一個資料源存在了哪里,到底有什么用處,現在在上面ConfigurationBuilder類里面看到存在了Sources 集合里面,然后configBuilder.Build()
去呼叫的時候遍歷資料源(Sources )集合,緊接著source (IConfigurationSource)呼叫了Build方法構建了一個configurationProvider物件存到configurationProviderList集合里面,最后在回傳一個ConfigurationRoot物件的建構式里面傳遞了configurationProviderList集合去執行,
貼上ConfigurationRoot的原始碼:
public class ConfigurationRoot : IConfigurationRoot, IConfiguration, IDisposable { private readonly IList<IConfigurationProvider> _providers; private readonly IList<IDisposable> _changeTokenRegistrations; /// <summary>Initializes a Configuration root with a list of providers.</summary> /// <param name="providers">The <see cref="T:Microsoft.Extensions.Configuration.IConfigurationProvider" />s for this configuration.</param> public ConfigurationRoot(IList<IConfigurationProvider> providers) { if (providers == null) throw new ArgumentNullException(nameof (providers)); this._providers = providers; this._changeTokenRegistrations = (IList<IDisposable>) new List<IDisposable>(providers.Count); foreach (IConfigurationProvider provider in (IEnumerable<IConfigurationProvider>) providers) { IConfigurationProvider p = provider; p.Load(); this._changeTokenRegistrations.Add(ChangeToken.OnChange((Func<IChangeToken>) (() => p.GetReloadToken()), (Action) (() => this.RaiseChanged()))); } } }
看到沒,最后providers去呼叫了load方法,
結語
就上面的控制臺代碼來說IConfigurationSource對應的實作是JsonConfigurationSource;IConfigurationProvider,抽象類ConfigurationProvider對應的實作為JsonConfigurationProvider,如果我們要換成別的檔案格式呢?比如ini,怎樣自定義配置源呢?大家可以先想想,其實也很簡單,下次跟大家分享,
最后說真的,.netCore原始碼真的特別優秀,很值得花一番時間去看看!從其中可以學到許多架構知識!
作者:realyrare
出處:https://www.cnblogs.com/mhg215/
聲援博主:如果您覺得文章對您有幫助,請點擊文章末尾的【關注我】吧!
別忘記點擊文章右下角的【推薦】支持一波,~~~///(^v^)\\\~~~ .
如果您有其他問題,也歡迎關注我的公眾號,可以聯系我一起交流切磋!
本文著作權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段宣告,且在文章頁面明顯位置給出原文連接,否則保留追究法律責任的權利,
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/349456.html
標籤:.NET Core
