.Net 4.X 提前用上 .Net Core 的配置模式以及熱重載配置

1. 前言

在提倡微服務及 Serverless 越來越普及的當下,傳統 .Net 應用的配置模式往往依賴於一個名爲 web.config 的 XML 文件,在可擴展性和可讀性與時代脫節了。當然,我不會慫恿一下子把所有應用遷移到 .Net Core 上,本文將在儘量不引入 .Net Core 開發模式的前提下,獲得最大的利益。

在開始之前,我們還是先說說 .Net Core 的配置模式有何優勢以及最少的依賴。

1.1 .Net Core 配置模式的優勢

  • 支持多種格式,如 Json、ini、Yaml、系統環境變量等
  • 不再依賴於 web.config ,可同時使用多種配置格式
  • 支持熱重載配置,修改配置可以不用重啓應用

1.2 最少依賴

nuget install  Microsoft.Extensions.Configuration
nuget install Microsoft.Extensions.Configuration.Binder
nuget install Castle.Windsor (其他 IOC 框架均可)

如果你安裝的是最新的包,可能會遇到 Microsoft.Extensions.Configuration 系列 Nuget 包無法安裝的問題,這主要取決當前應用的 .Net 版本,請參考下圖,安裝對應的版本(目前不支持 .Net 4.5 以下的應用)。
版本兼容情況

由於本人喜歡可讀性高的 Json 文件,所以還安裝 Microsoft.Extensions.Configuration.Json 的 Nuget 包。

2. 示例教程

本文在 Abp 2.1.3 的基礎上實現 .Net Core 的配置模式以及熱重載配置,更詳細的過程可參照我在 Github 上的 提交歷史

2.1 加載配置

.Net Core 配置模式的核心是一個名爲 IConfigurationRoot 的接口對象,需要在應用入口中加載各種配置格式後創建一個 IConfigurationRoot 的實例,在傳統的 .Net Web 應用中是在 Global.asax.cs 中賦值。

// ConfigurationExtenion.AppConfiguration 爲一個靜態的 IConfigurationRoot 實例。
 ConfigurationExtenion.AppConfiguration = new ConfigurationBuilder()
                .AddJsonFile("appsetting.json", optional: false, reloadOnChange: true)
                .AddJsonFile("appsetting01.json", optional: true, reloadOnChange: false)
                .Build();

簡單地解釋一下,我們需要(在根目錄中) 有一個名爲 "appsetting.json" 的 Json 文件,被修改的同時會重載 ConfigurationExtenion.AppConfiguration 。與之相反的 "appsetting01.json" 則允許不存在,即使存在,被修改時不會重載配置。

2.2 讀取配置節點

加載配置後,我們需要把配置讀取出來,在 IConfigurationRoot 中所有配置的信息都是存在於一個個節點中,我們可以根據節點名稱來獲取對應的類型對象。

        /// <summary>
        /// 獲取節點配置
        /// </summary>
        /// <typeparam name="TService"></typeparam>
        /// <param name="config"></param>
        /// <param name="sectionName"></param>
        /// <returns></returns>
        internal static TService GetSectionObject<TService>(IConfigurationRoot config, String sectionName)
        {
            var section = config.GetSection(sectionName);
            if (section == null)
            {
                throw new ArgumentException($" {sectionName} 未綁定,無法獲取到配置節點信息!");
            }
            return section.Get<TService>();
        }

假設我們有一個這樣的 "appsetting.json" 文件:

{
    "RedisConfiguration": {
      "InstanceDbId": 14,
      "InstanceRedisConnectionString": "127.0.0.1"
    },
    "MongoDbConfiguration": {
      "ConnectionString": "mongodb://127.0.0.1:27017/?connectTimeoutMS=300000",
      "DatatabaseName": "local"
    }
  }

如果我們要獲取 MongoDbConfiguration 下的 ConnectionString 的值,那麼我們可以這樣獲取:

var connectionString= GetSectionObject<String>(ConfigurationExtenion.AppConfiguration,"MongoDbConfiguration:ConnectionString");

2.3 設計配置類

在傳統的 .Net 應用程序中,我們往往會使用一個靜態變量去存放配置信息。而在有 IOC 的情況下,更好的方法是設計一個類來存放配置,如上面的 Json 文件我們可以設計如下兩個類(在 Visual Studio 選擇性黏貼 Json 會自動生成對象):

    public class RedisConfiguration
    {
        public int InstanceDbId { get; set; }
        public string InstanceRedisConnectionString { get; set; }
    }

    public class MongodbConfiguration
    {
        public string ConnectionString { get; set; }
        public string DatatabaseName { get; set; }
    }

2.4 註冊配置

爲了實現熱重載配置,而不是一層不變的值,我們在 IOC 中獲取配置類時,需要使用工廠方法獲取。在 Windsor 中可以這麼做:

        /// <summary>
        /// 註冊方法
        /// </summary>
        /// <typeparam name="TService"></typeparam>
        /// <param name="ioc"></param>
        /// <param name="factoryMethod"></param>
        private static void Register<TService>(IIocManager ioc, 
                                    Func<TService> factoryMethod) where TService : class
        {
            ioc.IocContainer
                .Register(
                    Component.For<TService>()
                        .UsingFactoryMethod(factoryMethod)
                        .LifestyleTransient() //這裏的生命週期是瞬時的,單例不可以嗎?
                );
        }

結合前面的獲取配置節點,我們可以把兩個靜態方法組合起來創造一個新的靜態方法,更方便我們使用。

        /// <summary>
        /// 註冊配置
        /// </summary>
        /// <typeparam name="TService"></typeparam>
        /// <param name="ioc"></param>
        /// <param name="config"></param>
        /// <param name="sectionName"></param>
        internal static void InitConfigService<TService>(IIocManager ioc, IConfigurationRoot config, String sectionName) where TService : class
        {
            Register(ioc, () =>
            {
                var service = GetSectionObject<TService>(config, sectionName);
                return service;
            });
        }

對於前面兩個配置類,我們可以這樣注入:

   ConfigurationExtenion.InitConfigService<RedisConfiguration>(IocManager,ConfigurationExtenion.AppConfiguration, "RedisConfiguration");
   ConfigurationExtenion.InitConfigService<MongodbConfiguration>(IocManager, ConfigurationExtenion.AppConfiguration, "MongoDbConfiguration");

2.5 獲取配置

我在這裏新增一個控制器方法獲取 RedisConfiguration 對象,該方法使用屬性注入獲取指定的配置類,並返回給頁面。

       public String GetRedisConfig()
        {
            var redisConfig = IocManager.Instance.Resolve<RedisConfiguration>();
            return redisConfig.ToJsonString();
        }

可以看到頁面顯示的與 Json 文件中結構一模一:
結果

2.6 熱重載配置

與 .Net Core 不一樣,在 .Net 4.X 的 Web 應用中只是稍微修改下 Json 文件都會讓整個應用重啓(修改 web.config 同樣會重啓),所以我將 "appsetting.json" 重命名爲 "appsetting.conf" ,在運行時修改某些值,並重新訪問控制器,可以看到對應的值變了。

效果

需要注意 IIS 的安全配置,Json 或者 ini 也許能直接通過 HTTP 獲取!

3. 結語

通過簡單的使用 .Net Core 配置模式,我們可以感受到其強大魅力,如果你對 .Net Core 更多的瞭解,以及感受兩者的對比,可以對照我之前寫過的一篇文章。對於熱重載配置,.Net Core 中更多是使使用IOptionsSnapshot,而爲了儘量少地引入 .Net Core 的特性,在這裏只是簡單地用了 IOC 的特性。當然,在不使用任何 IOC 的情況下(如果你害怕引入 IOC ),定義一個 IConfigurationRoot 的全局靜態實例,也不失爲一個折中的方案。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章