<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          NetCore配置框架詳解

          共 11130字,需瀏覽 23分鐘

           ·

          2020-10-02 18:09

          前言

          這邊文章主要是對netcore中的配置框架做一個實戰(zhàn)詳解,篇幅較長內(nèi)容涉及比較多,請耐心閱讀并進行嘗試,均采用控制臺程序進行展示。
          環(huán)境:
          netcore 3.1.4
          win10
          vs 2019 16.5.5

          1、依賴項安裝

          以下所有依賴項是包含了配置框架中主要用到的依賴項。
          主要是以下兩個包:

          • Microsoft.Extensions.Configuration.Abstractions 配置框架抽象包

          • Microsoft.Extensions.Configuration 實現(xiàn)包

          配置框架中幾個重要的對象:

          • IConfigurationBuilder

          • IConfigurationRoot

          • IConfiguration

          • IConfigurationProvider

          其他的都主要是配置框架中的擴展項。下面介紹到相關的時候會給出是依賴于那個包,耐心閱讀。

            <ItemGroup>
          <PackageReference Include="Microsoft.Extensions.Configuration" Version="3.1.4" />
          <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="3.1.4" />
          <PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="3.1.4" />
          <PackageReference Include="Microsoft.Extensions.Configuration.CommandLine" Version="3.1.4" />
          <PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="3.1.4" />
          <PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="3.1.4" />
          <PackageReference Include="Microsoft.Extensions.Configuration.Ini" Version="3.1.4" />
          <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="3.1.4" />
          <PackageReference Include="Microsoft.Extensions.Hosting" Version="3.1.4" />
          <PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="3.1.4" />
          <PackageReference Include="Microsoft.Extensions.Options" Version="3.1.4" />
          </ItemGroup>

          2、根路徑輸出

          在使用添加文件的配置時需要設置程序根目錄,這里做了一個添加根目錄的匯總以及輸出展示。

          可以看到AppContext.BaseDirectoryAppDomain.CurrentDomain.BaseDirectory輸出了根目錄絕對路徑(后面多了\),而Environment.CurrentDirectoryDirectory.GetCurrentDirectory()輸出后面沒有\。
          \的意義表示bin文件夾里的所有文件和文件夾;反之,bin文件夾里的所有文件和與bin同節(jié)的文件

          一般都是使用Directory.GetCurrentDirectory()進行設置根目錄,這里嘗試了添加文件配置的時候四種方式都是可以的。

          3、注冊各種配置方式

          下面代碼塊展示了添加各種配置的方式。
          添加文件的時候有三個參數(shù),第一個參數(shù)路徑,第二個參數(shù)該文件是否可選,第三個參數(shù)是否熱更新(文件發(fā)生變化配置自動更新)。

          var builder = new ConfigurationBuilder();
          builder.SetBasePath(Directory.GetCurrentDirectory());
          //Microsoft.Extensions.Configuration.Json包
          builder.AddJsonFile("appsetting.json", optional: false, reloadOnChange: true);
          //Microsoft.Extensions.Configuration.Ini包
          builder.AddIniFile("appsetting.ini", optional: false, reloadOnChange: true);
          //Microsoft.Extensions.Configuration包
          builder.AddInMemoryCollection(new Dictionary<string, string>
          {
          {"Name","Jonny" },
          {"Age","25" },
          {"Gender","Male" },
          {"Address:Address1","重慶奉節(jié)" },
          {"Address:Address2","重慶渝北" }
          });
          //Microsoft.Extensions.Configuration.CommandLine包
          builder.AddCommandLine(args);
          //Microsoft.Extensions.Configuration.EnvironmentVariables包
          builder.AddEnvironmentVariables();
          方法依賴包說明
          AddJsonFileMicrosoft.Extensions.Configuration.Json通過Json文件,aspnet core中最常用的配置方式appsetting.json
          AddIniFileMicrosoft.Extensions.Configuration.Iniini文件配置方式
          AddInMemoryCollectionMicrosoft.Extensions.Configuration內(nèi)存中配置
          AddCommandLineMicrosoft.Extensions.Configuration.CommandLine命令行
          AddEnvironmentVariablesMicrosoft.Extensions.Configuration.EnvironmentVariables環(huán)境變量,環(huán)境變量在windows中使用key1:key2:value的方式進行層級之分。而在Linux中:使用雙下劃線__代替,在編寫代碼的時候照樣使用:取,只是在添加環(huán)境變量的時候使用__

          3.1 內(nèi)存配置的讀取

          上面在內(nèi)容中添加了配置,這里進行讀取。

          #region 內(nèi)存配置的讀取
          {
          Console.WriteLine("=======================內(nèi)存配置的讀取======================");
          var configRoot = builder.Build();
          Console.WriteLine($"Name:{configRoot["Name"]}");
          Console.WriteLine($"Age:{configRoot["Age"]}");
          Console.WriteLine($"Gender:{configRoot["Gender"]}");
          //這里讀取出Address節(jié)點下內(nèi)容
          var addressRoot = configRoot.GetSection("Address");
          Console.WriteLine($"Address__Address1:{addressRoot["Address1"]}");
          Console.WriteLine($"Address__Address2:{addressRoot["Address2"]}");
          }
          #endregion

          通過IConfigurationGetSection()方法獲取節(jié)點塊,再通過配置Key名稱獲取對應的值,在IConfiguration中也可以通過:來分層讀取,這里介紹了使用key的方式讀取后下面在介紹其他方式的時候就不做過多介紹。

          例如:
          這里的Address1可以直接通過

          configRoot["Address:Address1"]

          讀取。

          最終的結(jié)果展示:

          3.2 JSON配置的讀取

          配置文件appsetting.json內(nèi)容如下:

          {
          "AppConfig": {
          "RemoteService": "http://localhost:44371",
          "Hospital": {
          "Name": "重慶市婦幼保健院 一分院",
          "Tel": "023-56781234",
          "Level": 3
          }
          }
          }

          這里層級比較深,專為上面的GetSection()方法和:再一次驗證。
          測試代碼:

          #region JSON配置的讀取
          {
          Console.WriteLine("=======================JSON配置的讀取======================");
          var configRoot = builder.Build();
          var appConfigRoot = configRoot.GetSection("AppConfig");
          Console.WriteLine($"AppConfig__RemoteService:{appConfigRoot["RemoteService"]}");
          var hospitalRoot = appConfigRoot.GetSection("Hospital");
          Console.WriteLine($"AppConfig__Hospital__Name:{hospitalRoot["Name"]}");
          Console.WriteLine($"AppConfig__Hospital__Tel:{hospitalRoot["Tel"]}");
          //這里通過一步到位獲取一個值
          Console.WriteLine($"AppConfig__Hospital__Level:{configRoot["AppConfig:Hospital:Level"]}");
          //解釋一下這里為什么用__來分隔,因為在Linux中:就是用雙下劃線__來替換的
          }
          #endregion

          最終效果展示:

          3.3 INI配置的讀取

          文件內(nèi)容:

          [服務器]
          RemoteService=http://localhost:44372
          Account=admin

          測試代碼:

          #region INI配置的讀取
          {
          Console.WriteLine("=======================INI配置的讀取======================");
          var configRoot = builder.Build();
          Console.WriteLine($"服務器__RemoteService:{configRoot["服務器:RemoteService"]}");
          Console.WriteLine($"服務器__Account:{configRoot["服務器:Account"]}");
          }
          #endregion

          3.4 命令行配置

          #region 命令行配置
          {
          Console.WriteLine("=======================命令行配置======================");
          //Microsoft.Extensions.Configuration.CommandLine包
          builder.AddCommandLine(args);
          var configRoot = builder.Build();
          Console.WriteLine($"CommandLine1:{configRoot["CommandLine1"]}");
          Console.WriteLine($"CommandLine2:{configRoot["CommandLine2"]}");
          }
          #endregion

          通過項目屬性–>調(diào)試–>添加命令行

          也可以使用launchSettings.json文件添加命令行

          {
          "profiles": {
          "Jonny.AllDemo.OptionsConfig": {
          "commandName": "Project",
          "commandLineArgs": "CommandLine1=key1 --CommandLine2=key2 /CommandLine3=key3 --c=newKey1",
          "environmentVariables": {
          "ASPNETCORE_ENVIRONMENT": "Development"
          }
          },
          "launchSetting.json": {
          "commandName": "Executable"
          }
          }
          }

          commandLineArgs節(jié)點配置命令行。

          在dotnet-cli中我們可以看到–help命也可以使用-h代替,那么我們也可以這樣操作。

          在上面的添加命令行方法AddCommandLine()第二個參數(shù)加入進去,使用-c代替CommandLine1命令。

          //使用 - c替換掉CommandLine1
          var replaceCommond = new Dictionary<string, string>
          {
          {"-c","CommandLine1" }
          };
          builder.AddCommandLine(args, replaceCommond);

          修改命令行:

          "commandLineArgs": "CommandLine1=key1  --CommandLine2=key2 /CommandLine3=key3 -c=newKey1"

          將上面的–c改成-c??梢钥吹捷敵鲋底兂闪?mark>newKey1,不再是==key1 ==

          3.5 環(huán)境變量

          1. 項目中添加環(huán)境變量

            一般在asp.net core直接在文件中通過environmentVariables添加環(huán)境變量。

          2. 系統(tǒng)環(huán)境變量

          3. 讀取

          4、實體綁定配置

          通過上面的各種方式來測試了配置框架中實現(xiàn),但是在開發(fā)中一般不會這么操作 ,而是通過實體的綁定來進行操作的,在實體中操作又要涉及到netcore 框架中的依賴注入,本能篇幅就不對依賴注入進行展開,后面會更新。

          這里我們定義配置實體IdentityClientOption,新增配置文件內(nèi)容。

          "IdentityClients": {
          "Default": {
          "GrantType": "password",
          "ClientId": "Jonny.AllDemo.OptionsConfig",
          "ClientSecret": "1q2w3E*",
          "UserName": "admin",
          "UserPassword": "1q2w3E*",
          "Authority": "https://localhost:44371",
          "Scope": "OptionsConfig"
          }
          }
          public class IdentityClientOption
          {
          public string GrantType { get; set; }
          public string ClientId { get; set; }
          public string ClientSecret { get; set; }
          public string UserName { get; set; }
          public string UserPassword { get; set; }
          public string Authority { get; set; }
          public string Scope { get; set; }

          }


          為了方便測試我們這里增加一個IUserAppService接口,其實也是可以直接通過ServiceProvider獲取進行測試,但是在官方文檔中這樣的操作是返回的,應該使用注入的方式進行獲取。
          測試服務代碼:

          public class AppUser
          {
          public string Name { get; set; }
          public int Age { get; set; }
          public Gender Gender { get; set; }
          public override string ToString()
          {
          return $"Name:{Name},Age:{Age},Gender:{Gender}";
          }
          }
          public enum Gender
          {
          Unkonw,
          Male,
          Famale
          }
          public interface IUserAppService
          {
          List<AppUser> GetUsers();
          }
          public class UserAppService : IUserAppService
          {
          protected readonly IdentityClientOption _identityMonitor;
          protected readonly IdentityClientOption _identitySnapshot;
          protected readonly IdentityClientOption _identity;
          public UserAppService(IOptionsMonitor<IdentityClientOption> optionsMonitor,
          IOptionsSnapshot
          <IdentityClientOption> optionsSnapshot,
          IOptions
          <IdentityClientOption> options)
          {
          _identityMonitor = optionsMonitor?.CurrentValue;
          _identitySnapshot = optionsSnapshot?.Value;
          _identity = options?.Value;
          Console.WriteLine($"Monitor:\t{_identityMonitor?.ToString()}");
          Console.WriteLine($"Snapshot:\t{_identitySnapshot?.ToString()}");
          Console.WriteLine($"Options:\t{_identity?.ToString()}");
          }
          public List<AppUser> GetUsers()
          {
          return new List<AppUser>
          {
          new AppUser
          {
          Name="Jonny",
          Age=25,
          Gender=Gender.Male
          }
          };
          }
          }

          在測試之前我這里引入Host概念,這是asp.net core中的宿主靜類。
          宿主主要有兩個依賴包:

          • Microsoft.Extensions.Hosting.Abstractions 抽象包

          • Microsoft.Extensions.Hosting 實現(xiàn)包

          測試代碼:

          #region 實體綁定+驗證+修改配置
          {
          var configRoot = builder.Build();
          Host.CreateDefaultBuilder(args).ConfigureServices(services =>
          {
          services.AddSingleton<IUserAppService, UserAppService>();
          var defaultOption = configRoot.GetSection("IdentityClients:Default");
          //綁定實體
          {
          //這種方式任何生命周期注冊都可以使用IOptions IOptionsSnapshot IOptionsMonitor
          services.AddOptions<IdentityClientOption>().Bind(defaultOption);
          //使用Configure的方式綁定
          services.Configure<IdentityClientOption>(defaultOption);
          //測試獲取
          var userApp = services.BuildServiceProvider().GetRequiredService<IUserAppService>();
          }
          }).Build().Run();
          }

          通過ServiceProvider獲取容器中的服務時會自動調(diào)用構(gòu)造,具體服務之間的構(gòu)造先后順序以及涉及到的生命周期我這里就不再展開了。下面結(jié)果可以看到配置文件綁定到了IdentityClientOption中。
          上面代碼中可以看到使用了兩種方式綁定:

          //這種方式任何生命周期注冊都可以使用IOptions IOptionsSnapshot IOptionsMonitor
          services.AddOptions<IdentityClientOption>().Bind(defaultOption);
          //使用Configure的方式綁定
          services.Configure<IdentityClientOption>(defaultOption);

          而使用IOptionsMonitor時使用CurrentValue,其他兩個使用Value。

          4.1 實體綁定驗證

          4.1.1 Validate()方法驗證

          這里測試之間將前面的appsetting.json內(nèi)容GrantType值改成Client。

          //驗證--通過Validate()方法驗證
          services.AddOptions<IdentityClientOption>()
          .Validate(option =>
          {
          if (option.GrantType == "password")
          {
          return true;
          }
          return false;
          });

          下面會拋出一個錯誤,這樣配置中加入驗證是為了程序部署的時候配置不成功不讓啟動,這樣保證了程序正確性。

          4.1.2 ?實現(xiàn)IValidateOptions添加驗證

          這種驗證方式也是常用的驗證方式,這樣可以對復雜的配置項進行驗證,驗證代碼統(tǒng)一 管理,單一職責性。

          public class IdentityValitate : IValidateOptions<IdentityClientOption>
          {
          public ValidateOptionsResult Validate(string name, IdentityClientOption options)
          {
          if (options.GrantType=="password")
          {
          return ValidateOptionsResult.Success;
          }
          return ValidateOptionsResult.Fail("驗證方式不是password模式");
          }
          }

          添加驗證到服務中:

          //驗證-- > 通過注冊Validator來驗證
          services.AddSingleton<IValidateOptions<IdentityClientOption>, IdentityValitate>();

          4.1.3 ValidateDataAnnotations()

          安裝Microsoft.Extensions.Options.DataAnnotations包,對于這個包相比大家都不是很陌生,以前MVC開發(fā)中模型驗證都會用到DataAnnotations下的特性。
          配置屬性上增加驗證。

          5、配置熱更新

          有時候項目上線后需要用到不停機的情況下修改配置,這樣就要用到熱更新。

          5.1 IChangeToken注冊

          #region 使用ChangeToken熱更新監(jiān)控配置改變
          {
          var configRoot = builder.Build();
          var token = configRoot.GetReloadToken();
          token.RegisterChangeCallback(state =>
          {
          Console.WriteLine($"【{configRoot["AppConfig:Hospital:Name"]}】服務配置發(fā)生了變化一次");
          var token1 = configRoot.GetReloadToken();
          token1.RegisterChangeCallback(state1=>
          {
          Console.WriteLine($"【{configRoot["AppConfig:Hospital:Name"]}】服務配置發(fā)生了變化兩次");
          }, configRoot);
          }, configRoot);
          Console.WriteLine($"【{configRoot["AppConfig:Hospital:Name"]}】服務啟動完成");
          }
          #endregion

          上面我直接注冊了兩次RegisterChangeCallback()方法,當我第一次修改文件Name值保存會輸出更改值,當后面再更改后就沒有發(fā)生變化。

          所以,使用IChangeToken注冊的只能觸發(fā)第一次的更改變化,這樣顯然是達不到要求的。下面會接受另外的方式。

          5.2 靜態(tài)類ChangeToken

          使用靜態(tài)類ChangeTokenOnChange()方法進行監(jiān)控。

          ChangeToken.OnChange(() => configRoot.GetReloadToken(), () =>
          {
          Console.WriteLine($"【{configRoot["AppConfig:Hospital:Name"]}】服務配置發(fā)生了變化");
          });

          修改bin\Debug文件夾下的配置文件進行測試,測試的過程中發(fā)現(xiàn)只要觸發(fā)了文件的保存操作都會觸發(fā)OnChange()方法,無論內(nèi)容是否變化,不知這里是一個什么原因???

          注意,由于我這里測試使用的是控制臺應用程序,需要修改bin\Debug文件下面的配置文件才能生效,但是使用asp.net core就不用,直接修改項目中的配置文件就可以。

          5.3 IOptionsSnapshot和IOptionsMonitor

          由于我這里是控制臺應用程序,我這里采用RegisterChangeCallback()方法來借助測試,通過更改后重新獲取IUserAppService,輸出可以看見

          //測試獲取
          var userApp = services.BuildServiceProvider().GetRequiredService<IUserAppService>();
          var token = configRoot.GetReloadToken();
          token.RegisterChangeCallback(state =>
          {
          var userNewApp = services.BuildServiceProvider().GetRequiredService<IUserAppService>();
          }, configRoot);

          這里包括IOptions也更新了,不知道是不是控制臺應用程序的原因????


          出現(xiàn)上面這么一個問題后我立馬用asp.net core做了一個測試。測試結(jié)果表明`IOptions并不會更新,那么為什么上面就更新了呢??程序那點沒寫對??知道的大佬歡迎指正下,我下來也會去摸索。

          6 總結(jié)

          以上所有內(nèi)容的測試和文章記錄多多少少花了兩個晚上的時間,希望能夠快速的給園友們帶來幫助,寫這篇文章讓我對配置框架有了一個更深的認知,寫了差不多3個小時左右,寫作不易希望得園友們的支持點贊和關注。
          文章中提到了依賴注入也使用了依賴注入的測試,后面會對依賴注入框架分享一篇文章。

          瀏覽 58
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产欧美精品AAAAAA片 | 天堂 在线8 | 国产精品久久夜色 | 欧美91在线 | 久久久久久成人无码 |