.NET Core 3.0之深入源碼理解Configuration(三)

来源:https://www.cnblogs.com/edison0621/archive/2019/05/19/10891365.html
-Advertisement-
Play Games

寫在前面 上一篇文章討論了文件型配置的基本內容,本篇內容討論JSON型配置的實現方式,理解了這一種配置類型的實現方式,那麼其他類型的配置實現方式基本可以觸類旁通。看過了上一篇文章的朋友,應該看得出來似曾相識。此圖主要表達了文件型配置的實現,當然其他配置,包括自定義配置,都會按照這樣的方式去實現。 J... ...


寫在前面

上一篇文章討論了文件型配置的基本內容,本篇內容討論JSON型配置的實現方式,理解了這一種配置類型的實現方式,那麼其他類型的配置實現方式基本可以觸類旁通。看過了上一篇文章的朋友,應該看得出來似曾相識。此圖主要表達了文件型配置的實現,當然其他配置,包括自定義配置,都會按照這樣的方式去實現。

繪圖3

JSON配置組件的相關內容

該組件有四個類

  • JsonConfigurationExtensions
  • JsonConfigurationSource
  • JsonConfigurationFileParser
  • JsonConfigurationProvider

這四個類相互合作,職責明確,共同將JSON類型的配置載入到記憶體中,供相應的系統使用。

JsonConfigurationFileParser

該類是一個內部類,擁有一個私有的構造方法,意味著該類無法在其他地方進行實例化,只能在自己內部使用。它只有一個公共方法,並且是靜態的,如下所示

   1:  public static IDictionary<string, string> Parse(Stream input) => new JsonConfigurationFileParser().ParseStream(input);

該方法通過讀取輸入數據流,將其轉化為字典類型的配置數據,該字典類型是SortedDictionary類型的,並且不區分大小寫,此處需要註意。這也呼應了之前所說的.NET Core Configuration對外使用的時候,都是以字典方式去提供給外界使用的。

那麼,這個類是如何將數據流轉化為JSON的呢,我們繼續閱讀源碼

   1:  private IDictionary<string, string> ParseStream(Stream input)
   2:  {
   3:      _data.Clear();
   4:   
   5:      using (var reader = new StreamReader(input))
   6:      using (JsonDocument doc = JsonDocument.Parse(reader.ReadToEnd(), new JsonReaderOptions { CommentHandling = JsonCommentHandling.Skip }))
   7:      {
   8:          if (doc.RootElement.Type != JsonValueType.Object)
   9:          {
  10:              throw new FormatException(Resources.FormatError_UnsupportedJSONToken(doc.RootElement.Type));
  11:          }
  12:          VisitElement(doc.RootElement);
  13:      }
  14:   
  15:      return _data;
  16:  }

通過源碼,我們知道,此處使用了JsonDocument處理StreamReader數據,JsonDocument又是什麼呢,通過命名空間我們知道,它位於System.Text.Json中,這是.NET Core原生的處理JSON的組件,有興趣的朋友可以去翻翻MSDN或者GitHub查找相關資料。此處不做過多說明。

VisitElement方法主要是遍歷JsonElement.EnumerateObject()方法中的對象集合,此處採用Stack<string>實例進行數據安全方面的控制。其中VisitValue是一個在處理json時相當全面的方法,說它全面是因為它考慮到了JSON值的幾乎所有類型:

  • JsonValueType.Object
  • JsonValueType.Array
  • JsonValueType.Number
  • JsonValueType.String
  • JsonValueType.True
  • JsonValueType.False
  • JsonValueType.Null

當然,該方法,並不會很傻的處理每一種類型,主要是針對Object和Array類型進行了遞歸遍歷,以便在諸如Number、String等的簡單類型時跳出遞歸,並存放到字典中,需要再次強調的是,存放在字典中的值是以String類型存儲的。

至此,JsonConfigurationFileParser完成了從文件讀取內容並轉化為鍵值對的工作。

JsonConfigurationSource

這個類比較簡單,因為繼承自FileConfigurationSource,如前文所說,FileConfigurationSource類已經做了初步的實現,只提供了一個Build方法交給子類去重寫。其返回值是JsonConfigurationProvider實例。

   1:  /// <summary>
   2:  /// Represents a JSON file as an <see cref="IConfigurationSource"/>.
   3:  /// </summary>
   4:  public class JsonConfigurationSource : FileConfigurationSource
   5:  {
   6:      /// <summary>
   7:      /// Builds the <see cref="JsonConfigurationProvider"/> for this source.
   8:      /// </summary>
   9:      /// <param name="builder">The <see cref="IConfigurationBuilder"/>.</param>
  10:      /// <returns>A <see cref="JsonConfigurationProvider"/></returns>
  11:      public override IConfigurationProvider Build(IConfigurationBuilder builder)
  12:      {
  13:          EnsureDefaults(builder);
  14:          return new JsonConfigurationProvider(this);
  15:      }
  16:  }

此處的EnsureDefaults()方法,主要是設置FileProvider實例以及指定載入類型的異常處理方式。

JsonConfigurationProvider

這個類也很簡單,它繼承於FileConfigurationProvider,FileConfigurationProvider本身也已經通用功能進行了抽象實現,先看一下這個類的源碼

   1:  /// <summary>
   2:  /// A JSON file based <see cref="FileConfigurationProvider"/>.
   3:  /// </summary>
   4:  public class JsonConfigurationProvider : FileConfigurationProvider
   5:  {
   6:      /// <summary>
   7:      /// Initializes a new instance with the specified source.
   8:      /// </summary>
   9:      /// <param name="source">The source settings.</param>
  10:      public JsonConfigurationProvider(JsonConfigurationSource source) : base(source) { }
  11:   
  12:      /// <summary>
  13:      /// Loads the JSON data from a stream.
  14:      /// </summary>
  15:      /// <param name="stream">The stream to read.</param>
  16:      public override void Load(Stream stream)
  17:      {
  18:          try {
  19:              Data = JsonConfigurationFileParser.Parse(stream);
  20:          } catch (JsonReaderException e)
  21:          {
  22:              throw new FormatException(Resources.Error_JSONParseError, e);
  23:          }
  24:      }
  25:  }

其構造函數的傳入參數類型是JsonConfigurationSource,這和JsonConfigurationSource.Build()方法中的return new JsonConfigurationProvider(this)代碼片段相呼應。

JsonConfigurationProvider所重寫的方法,調用的是JsonConfigurationFileParser.Parse(stream)方法,所以該類顯得非常的輕量。

JsonConfigurationExtensions

這個方法,大家就更熟悉了,我們平時所使用的AddJsonFile()方法,就是在這個擴展類中進行擴展的,其返回值是IConfigurationBuilder類型,其核心方法源碼如下所示

   1:  /// <summary>
   2:  /// Adds a JSON configuration source to <paramref name="builder"/>.
   3:  /// </summary>
   4:  /// <param name="builder">The <see cref="IConfigurationBuilder"/> to add to.</param>
   5:  /// <param name="provider">The <see cref="IFileProvider"/> to use to access the file.</param>
   6:  /// <param name="path">Path relative to the base path stored in 
   7:  /// <see cref="IConfigurationBuilder.Properties"/> of <paramref name="builder"/>.</param>
   8:  /// <param name="optional">Whether the file is optional.</param>
   9:  /// <param name="reloadOnChange">Whether the configuration should be reloaded if the file changes.</param>
  10:  /// <returns>The <see cref="IConfigurationBuilder"/>.</returns>
  11:  public static IConfigurationBuilder AddJsonFile(this IConfigurationBuilder builder, IFileProvider provider, string path, bool optional, bool reloadOnChange)
  12:  {
  13:      if (builder == null)
  14:      {
  15:          throw new ArgumentNullException(nameof(builder));
  16:      }
  17:      if (string.IsNullOrEmpty(path))
  18:      {
  19:          throw new ArgumentException(Resources.Error_InvalidFilePath, nameof(path));
  20:      }
  21:   
  22:      return builder.AddJsonFile(s =>
  23:      {
  24:          s.FileProvider = provider;
  25:          s.Path = path;
  26:          s.Optional = optional;
  27:          s.ReloadOnChange = reloadOnChange;
  28:          s.ResolveFileProvider();
  29:      });
  30:  }

不過,大家不要看這個方法的代碼行數很多,就認為,其他方法都重載與該方法,其實該方法重載自

   1:  /// <summary>
   2:  /// Adds a JSON configuration source to <paramref name="builder"/>.
   3:  /// </summary>
   4:  /// <param name="builder">The <see cref="IConfigurationBuilder"/> to add to.</param>
   5:  /// <param name="configureSource">Configures the source.</param>
   6:  /// <returns>The <see cref="IConfigurationBuilder"/>.</returns>
   7:  public static IConfigurationBuilder AddJsonFile(this IConfigurationBuilder builder, Action<JsonConfigurationSource> configureSource)
   8:      => builder.Add(configureSource);

這個方法最終調用的還是IConfigurationBuilder.Add()方法

總結

通過介紹以上JSON Configuration組件的四個類,我們知道了,該組件針對JSON格式的文件的處理方式,不過由於其實文件型配置,其抽象實現已經在文件型配置擴展實現。

從這裡,我們可以學習一下,如果有一天我們需要擴展遠程配置,比如Consul、ZK等,我們也可以考慮並採用這種架構的設計方式。另外在JSON Configuration組件中,.NET Core將專有型功能方法的處理進行了聚合,並聚焦關註點的方式也值得我們學習。

最後JsonConfigurationFileParser中給了我們一種關於Stream轉換成JSON的實現,我們完全可以把這個類當成工具類去使用。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • List集合有序、元素可重覆。以元素的添加順序作為集合的排列順序,用下標索引集合中的元素。 List因為使用下標索引元素,所以元素可重覆。Set使用元素本身來索引,所以元素不能重覆。 List的繼承關係: List繼承了Collection的所有方法,也有自身的一些方法(下標操作): void ad ...
  • 1.什麼是列表? 列表是由一組按特定順序排列的元素組成。 2.如何表示? 在Python中用方括弧([ ])來表示列表。慄子如下: 控制台列印如下: 沒錯,它將列印列表的內部表示,包括方括弧。 3.如何去訪問列表元素? console(控制台): 所以說訪問列表的元素,可以在列表名後加方括弧,方括弧 ...
  • hasNextInt() :判斷是否還有下一個輸入項,其中Xxx可以是Int,Double等。如果需要判斷是否包含下一個字元串,則可以省略Xxx nextInt(): 獲取下一個輸入項。Xxx的含義和上個方法中的Xxx相同,預設情況下,Scanner使用空格,回車等作為分隔符 public stat ...
  • 1. 是否保證線程安全: ArrayList 和 LinkedList 都是不同步的,也就是不保證線程安全; 2. 底層數據結構: Arraylist 底層使用的是Object數組;LinkedList 底層使用的是雙向鏈表數據結構(JDK1.6之前為迴圈鏈表,JDK1.7取消了迴圈。註意雙向鏈表和 ...
  • 1. 前言 對WPF來說ContentControl和 "ItemsControl" 是最重要的兩個控制項。 顧名思義,ItemsControl表示可用於呈現一組Item的控制項。大部分時候我們並不需要自定義ItemsControl,因為WPF提供了一大堆ItemsControl的派生類:Headere ...
  • 在SQL語句查詢過程中,Sqlserver支持使用LEFT()、RIGHT()、SUBSTRING()等幾個函數對字元串進行截取操作,其中Left函數表示從開始字元向後截取多少個字元,Right函數表示從最後位置向前截取多少個字元,SUBSTRING()則可指定截取的起始位置以及截取長度。此文著重介 ...
  • 在C#語言程式開發過程中,很多時候需要對字元串對象的前後空格進行去除,此時就需要使用到Trim()方法來實現這個功能,Trim()方法可以快速去除字元串前端和後端的所有空格。 例如有個字元:string str=" Abc "; 則需要去除字元串前後空格的話,則可以採用:str=str.Trim() ...
  • Dapper優勢和缺點 優點 高性能、易排查、易運維、靈活可控 缺點 和EF相比,手寫sql當修改表結構不易發現bug。 習慣了EF後再來使用Dapper,會很難適應那種沒有了強類型的安全感。不過可以用單元測和心細來避免。 資料庫連接 問題:IDbConnection需不需要手動Open打開連接 答 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...