<span id="mktg5"></span>

<i id="mktg5"><meter id="mktg5"></meter></i>

        <label id="mktg5"><meter id="mktg5"></meter></label>
        最新文章專題視頻專題問答1問答10問答100問答1000問答2000關鍵字專題1關鍵字專題50關鍵字專題500關鍵字專題1500TAG最新視頻文章推薦1 推薦3 推薦5 推薦7 推薦9 推薦11 推薦13 推薦15 推薦17 推薦19 推薦21 推薦23 推薦25 推薦27 推薦29 推薦31 推薦33 推薦35 推薦37視頻文章20視頻文章30視頻文章40視頻文章50視頻文章60 視頻文章70視頻文章80視頻文章90視頻文章100視頻文章120視頻文章140 視頻2關鍵字專題關鍵字專題tag2tag3文章專題文章專題2文章索引1文章索引2文章索引3文章索引4文章索引5123456789101112131415文章專題3
        問答文章1 問答文章501 問答文章1001 問答文章1501 問答文章2001 問答文章2501 問答文章3001 問答文章3501 問答文章4001 問答文章4501 問答文章5001 問答文章5501 問答文章6001 問答文章6501 問答文章7001 問答文章7501 問答文章8001 問答文章8501 問答文章9001 問答文章9501
        當前位置: 首頁 - 科技 - 知識百科 - 正文

        淺析.Net Core中Json配置的自動更新

        來源:懂視網 責編:小采 時間:2020-11-27 22:34:29
        文檔

        淺析.Net Core中Json配置的自動更新

        淺析.Net Core中Json配置的自動更新:Pre 很早在看 Jesse 的 Asp.net Core快速入門 的課程的時候就了解到了在Asp .net core中,如果添加的Json配置被更改了,是支持自動重載配置的,作為一名有著嚴重造輪子情節的程序員,最近在折騰一個博客系統,也想造出一個這樣能自動更新以Mysql為數據源的
        推薦度:
        導讀淺析.Net Core中Json配置的自動更新:Pre 很早在看 Jesse 的 Asp.net Core快速入門 的課程的時候就了解到了在Asp .net core中,如果添加的Json配置被更改了,是支持自動重載配置的,作為一名有著嚴重造輪子情節的程序員,最近在折騰一個博客系統,也想造出一個這樣能自動更新以Mysql為數據源的

        Pre

        很早在看 Jesse 的 Asp.net Core快速入門 的課程的時候就了解到了在Asp .net core中,如果添加的Json配置被更改了,是支持自動重載配置的,作為一名有著嚴重"造輪子"情節的程序員,最近在折騰一個博客系統,也想造出一個這樣能自動更新以Mysql為數據源的ConfigureSource,于是點開了AddJsonFile這個拓展函數的源碼,發現別有洞天,蠻有意思,本篇文章就簡單地聊一聊Json config的ReloadOnChange是如何實現的,在學習ReloadOnChange的過程中,我們會把Configuration也順帶撩一把:grin:,希望對小伙伴們有所幫助.

        public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
         WebHost.CreateDefaultBuilder(args)
         .ConfigureAppConfiguration(option =>
         {
         option.AddJsonFile("appsettings.json",optional:true,reloadOnChange:true);
         })
         .UseStartup<Startup>();

        在Asp .net core中如果配置了json數據源,把reloadOnChange屬性設置為true即可實現當文件變更時自動更新配置,這篇博客我們首先從它的源碼簡單看一下,看完你可能還是會有點懵的,別慌,我會對這些代碼進行精簡,做個簡單的小例子,希望能對你有所幫助.

        一窺源碼

        AddJson

        首先,我們當然是從這個我們耳熟能詳的擴展函數開始,它經歷的演變過程如下.

         public static IConfigurationBuilder AddJsonFile(this IConfigurationBuilder builder,string path,bool optional,bool reloadOnChange)
         {
         return builder.AddJsonFile((IFileProvider) null, path, optional, reloadOnChange);
         }

        傳遞一個null的FileProvider給另外一個重載Addjson函數.

        敲黑板,Null的FileProvider很重要,后面要考:smile:.

         public static IConfigurationBuilder AddJsonFile(this IConfigurationBuilder builder,IFileProvider provider,string path,bool optional,bool reloadOnChange)
         {
         return builder.AddJsonFile((Action<JsonConfigurationSource>) (s =>
         {
         s.FileProvider = provider;
         s.Path = path;
         s.Optional = optional;
         s.ReloadOnChange = reloadOnChange;
         s.ResolveFileProvider();
         }));
         }

        把傳入的參數演變成一個Action委托給 JsonConfigurationSource 的屬性賦值.

         public static IConfigurationBuilder AddJsonFile(this IConfigurationBuilder builder, Action<JsonConfigurationSource> configureSource)
         {
         return builder.Add<JsonConfigurationSource>(configureSource);
         }

        最終調用的builder.add (action)方法.

         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);
         }

        在Add方法里,創建了一個Source實例,也就是JsonConfigurationSource實例,然后把這個實例傳為剛剛的委托,這樣一來,我們在最外面傳入的 "appsettings.json",optional:true,reloadOnChange:true 參數就作用到這個示例上了.

        最終,這個實例添加到builder中.那么builder又是什么?它能干什么?

        ConfigurationBuild

        前面提及的builder默認情況下是 ConfigurationBuilder ,我對它的進行了簡化,關鍵代碼如下.

        public class ConfigurationBuilder : IConfigurationBuilder
         {
         public IList<IConfigurationSource> Sources { get; } = new List<IConfigurationSource>();
        
         public IConfigurationBuilder Add(IConfigurationSource source)
         {
         Sources.Add(source);
         return this;
         }
        
         public IConfigurationRoot Build()
         {
         var providers = new List<IConfigurationProvider>();
         foreach (var source in Sources)
         {
         var provider = source.Build(this);
         providers.Add(provider);
         }
         return new ConfigurationRoot(providers);
         }
         }

        可以看到,這個builder中有個集合類型的Sources,這個Sources可以保存任何實現了 IConfigurationSource 的Source,前面聊到的 JsonConfigurationSource 就是實現了這個接口,常用的還有 MemoryConfigurationSource , XmlConfigureSource , CommandLineConfigurationSource 等.

        另外,它有一個很重要的build方法,這個build方法在 WebHostBuilder 方法執行 build 的時候也被調用,不要問我 WebHostBuilder.builder 方法什么執行的:joy:.

        public static void Main(string[] args)
         {
         CreateWebHostBuilder(args).Build().Run();
         }

        在ConfigureBuilder的方法里面就調用了每個Source的Builder方法,我們剛剛傳入的是一個 JsonConfigurationSource ,所以我們有必要看看JsonSource的builder做了什么.

        這里是不是被這些builder繞哭了? 別慌,下一篇文章中我會講解如何自定義一個ConfigureSoure,會把Congigure系列類UML類圖整理一下,應該會清晰很多.

        JsonConfigurationSource

        public class JsonConfigurationSource : FileConfigurationSource
         {
         public override IConfigurationProvider Build(IConfigurationBuilder builder)
         {
         EnsureDefaults(builder);
         return new JsonConfigurationProvider(this);
         }
         }

        這就是 JsonConfigurationSource 的所有代碼,未精簡,它只實現了一個Build方法,在Build內,EnsureDefaults被調用,可別小看它,之前那個空的FileProvider在這里被賦值了.

         public void EnsureDefaults(IConfigurationBuilder builder)
         {
         FileProvider = FileProvider ?? builder.GetFileProvider();
         }
         public static IFileProvider GetFileProvider(this IConfigurationBuilder builder)
         {
         return new PhysicalFileProvider(AppContext.BaseDirectory ?? string.Empty);
         }

        可以看到這個FileProvider默認情況下就是 PhysicalFileProvider ,為什么對這個 FileProvider 如此寵幸讓我花如此大的伏筆要強調它呢?往下看.

        JsonConfigurationProvider && FileConfigurationProvider

        在JsonConfigurationSource的build方法內,返回的是一個JsonConfigurationProvider實例,所以直覺告訴我,在它的構造函數內必有貓膩:confused:.

         public class JsonConfigurationProvider : FileConfigurationProvider
         {
         
         public JsonConfigurationProvider(JsonConfigurationSource source) : base(source) { }
        
         
         public override void Load(Stream stream)
         {
         try {
         Data = JsonConfigurationFileParser.Parse(stream);
         } catch (JsonReaderException e)
         {
         throw new FormatException(Resources.Error_JSONParseError, e);
         }
         }
         }

        看不出什么的代碼,事出反常必有妖~~

        看看base的構造函數.

         public FileConfigurationProvider(FileConfigurationSource source)
         {
         Source = source;
        
         if (Source.ReloadOnChange && Source.FileProvider != null)
         {
         _changeTokenRegistration = ChangeToken.OnChange(
         () => Source.FileProvider.Watch(Source.Path),
         () => {
         Thread.Sleep(Source.ReloadDelay);
         Load(reload: true);
         });
         }
         }

        真是個天才,問題就在這個構造函數里,它構造函數調用了一個 ChangeToken.OnChange 方法,這是實現ReloadOnChange的關鍵,如果你點到這里還沒有關掉,恭喜,好戲開始了.

        ReloadOnChange

        Talk is cheap. Show me the code (屁話少說,放 過來).

         public static class ChangeToken
         {
         public static ChangeTokenRegistration<Action> OnChange(Func<IChangeToken> changeTokenProducer, Action changeTokenConsumer)
         {
         return new ChangeTokenRegistration<Action>(changeTokenProducer, callback => callback(), changeTokenConsumer);
         }
         }

        OnChange方法里,先不管什么func,action,就看看這兩個參數的名稱,producer,consumer,生產者,消費者,不知道看到這個關鍵詞想到的是什么,反正我想到的是小學時學習食物鏈時的:snake:與:rat:.

        那么我們來看看這里的:snake:是什么,:rat:又是什么,還得回到 FileConfigurationProvider 的構造函數.

        可以看到生產者:rat:是:

        () => Source.FileProvider.Watch(Source.Path)

        消費者:snake:是:

        () => {
         Thread.Sleep(Source.ReloadDelay);
         Load(reload: true);
        }

        我們想一下,一旦有一條:rat:跑出來,就立馬被:snake:吃了,

        那我們這里也一樣,一旦有FileProvider.Watch返回了什么東西,就會發生Load()事件來重新加載數據.

        :snake:與:rat:好理解,可是代碼就沒那么好理解了,我們通過 OnChange 的第一個參數 Func<IChangeToken> changeTokenProducer 方法知道,這里的:rat:,其實是 IChangeToken .

        IChangeToken

         public interface IChangeToken
         {
         bool HasChanged { get; }
        
         bool ActiveChangeCallbacks { get; }
        
         IDisposable RegisterChangeCallback(Action<object> callback, object state);
         }

        IChangeToken的重點在于里面有個RegisterChangeCallback方法,:snake:吃:rat:的這件事,就發生在這回調方法里面.

        我們來做個:snake:吃:rat:的實驗.

        實驗1

         static void Main()
         {
         //定義一個C:\Users\liuzh\MyBox\TestSpace目錄的FileProvider
         var phyFileProvider = new PhysicalFileProvider("C:\\Users\\liuzh\\MyBox\\TestSpace");
        
         //讓這個Provider開始監聽這個目錄下的所有文件
         var changeToken = phyFileProvider.Watch("*.*");
        
         //注冊🐍吃🐀這件事到回調函數
         changeToken.RegisterChangeCallback(_=> { Console.WriteLine("老鼠被蛇吃"); }, new object());
        
         //添加一個文件到目錄
         AddFileToPath();
        
         Console.ReadKey();
        
         }
        
         static void AddFileToPath()
         {
         Console.WriteLine("老鼠出洞了");
         File.Create("C:\\Users\\liuzh\\MyBox\\TestSpace\\老鼠出洞了.txt").Dispose();
         }

        這是運行結果

        可以看到,一旦在監聽的目錄下創建文件,立即觸發了執行回調函數,但是如果我們繼續手動地更改(復制)監聽目錄中的文件,回調函數就不再執行了.

        這是因為changeToken監聽到文件變更并觸發回調函數后,這個changeToken的使命也就完成了,要想保持一直監聽,那么我們就在在回調函數中重新獲取token,并給新的token的回調函數注冊通用的事件,這樣就能保持一直監聽下去了.

        這也就是ChangeToken.Onchange所作的事情,我們看一下源碼.

         public static class ChangeToken
         {
         public static ChangeTokenRegistration<Action> OnChange(Func<IChangeToken> changeTokenProducer, Action changeTokenConsumer)
         {
         return new ChangeTokenRegistration<Action>(changeTokenProducer, callback => callback(), changeTokenConsumer);
         }
         }
         public class ChangeTokenRegistration<TAction>
         {
         private readonly Func<IChangeToken> _changeTokenProducer;
         private readonly Action<TAction> _changeTokenConsumer;
         private readonly TAction _state;
        
         public ChangeTokenRegistration(Func<IChangeToken> changeTokenProducer, Action<TAction> changeTokenConsumer, TAction state)
         {
         _changeTokenProducer = changeTokenProducer;
         _changeTokenConsumer = changeTokenConsumer;
         _state = state;
        
         var token = changeTokenProducer();
        
         RegisterChangeTokenCallback(token);
         }
        
         private void RegisterChangeTokenCallback(IChangeToken token)
         {
         token.RegisterChangeCallback(_ => OnChangeTokenFired(), this);
         }
        
         private void OnChangeTokenFired()
         {
         var token = _changeTokenProducer();
        
         try
         {
         _changeTokenConsumer(_state);
         }
         finally
         {
         // We always want to ensure the callback is registered
         RegisterChangeTokenCallback(token);
         }
         }
         }

        簡單來說,就是給token注冊了一個 OnChangeTokenFired 的回調函數,仔細看看 OnChangeTokenFired 里做了什么,總體來說三步.

        1.獲取一個新的token.
        2.調用消費者進行消費.
        3.給新獲取的token再次注冊一個OnChangeTokenFired的回調函數.

        如此周而復始~~

        實驗2

        既然知道了OnChange的工作方式,那么我們把實驗1的代碼修改一下.

         static void Main()
         {
         var phyFileProvider = new PhysicalFileProvider("C:\\Users\\liuzh\\MyBox\\TestSpace");
         ChangeToken.OnChange(() => phyFileProvider.Watch("*.*"),
         () => { Console.WriteLine("老鼠被蛇吃"); });
         Console.ReadKey();
         }

        執行效果看一下

        可以看到,只要被監控的目錄發生了文件變化,不管是新建文件,還是修改了文件內的內容,都會觸發回調函數,其實JsonConfig中,這個回調函數就是Load(),它負責重新加載數據,可也就是為什么Asp .net core中如果把ReloadOnchang設置為true后,Json的配置一旦更新,配置就會自動重載.

        PhysicalFilesWatcher

        那么,為什么文件一旦變化,就會觸發ChangeToken的回調函數呢? 其實 PhysicalFileProvider 中調用了 PhysicalFilesWatcher 對文件系統進行監視,觀察PhysicalFilesWatcher的構造函數,可以看到 PhysicalFilesWatcher 需要傳入 FileSystemWatcher , FileSystemWatchersystem.io 下的底層IO類,在構造函數中給這個Watcher的Created,Changed,Renamed,Deleted注冊EventHandler事件,最終,在這些EventHandler中會調用ChangToken的回調函數,所以文件系統一旦發生變更就會觸發回調函數.

         public PhysicalFilesWatcher(string root,FileSystemWatcher fileSystemWatcher,bool pollForChanges,ExclusionFilters filters)
         {
         this._root = root;
         this._fileWatcher = fileSystemWatcher;
         this._fileWatcher.IncludeSubdirectories = true;
         this._fileWatcher.Created += new FileSystemEventHandler(this.OnChanged);
         this._fileWatcher.Changed += new FileSystemEventHandler(this.OnChanged);
         this._fileWatcher.Renamed += new RenamedEventHandler(this.OnRenamed);
         this._fileWatcher.Deleted += new FileSystemEventHandler(this.OnChanged);
         this._fileWatcher.Error += new ErrorEventHandler(this.OnError);
         this.PollForChanges = pollForChanges;
         this._filters = filters;
         this.PollingChangeTokens = new ConcurrentDictionary<IPollingChangeToken, IPollingChangeToken>();
         this._timerFactory = (Func<Timer>) (() => NonCapturingTimer.Create(new TimerCallback(PhysicalFilesWatcher.RaiseChangeEvents), (object) this.PollingChangeTokens, TimeSpan.Zero, PhysicalFilesWatcher.DefaultPollingInterval));
         }

        如果你和我一樣,對源碼感興趣,可以從官方的 aspnet/Extensions 中下載源碼研究: https://github.com/aspnet/Extensions

        在下一篇文章中,我會講解如何自定義一個以Mysql為數據源的ConfigureSoure,并實現自動更新功能,同時還會整理Configure相關類的UML類圖,有興趣的可以關注我以便第一時間收到下篇文章.

        本文章涉及的代碼地址: https://github.com/liuzhenyulive/MiniConfiguration

        聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com

        文檔

        淺析.Net Core中Json配置的自動更新

        淺析.Net Core中Json配置的自動更新:Pre 很早在看 Jesse 的 Asp.net Core快速入門 的課程的時候就了解到了在Asp .net core中,如果添加的Json配置被更改了,是支持自動重載配置的,作為一名有著嚴重造輪子情節的程序員,最近在折騰一個博客系統,也想造出一個這樣能自動更新以Mysql為數據源的
        推薦度:
        • 熱門焦點

        最新推薦

        猜你喜歡

        熱門推薦

        專題
        Top
        主站蜘蛛池模板: 亚洲伊人tv综合网色| 国产亚洲精品精品国产亚洲综合| 亚洲国产精品无码成人片久久| 国产成人高清亚洲一区久久| 全免费一级毛片在线播放| 久久亚洲精品国产精品婷婷| 美女视频黄免费亚洲| 亚洲小说图区综合在线| 日韩免费a级在线观看| 久久亚洲中文字幕无码| 亚洲国产婷婷综合在线精品| yellow视频免费看| 亚洲va无码va在线va天堂| 免费国产黄网站在线观看可以下载| 内射干少妇亚洲69XXX| 97在线观看永久免费视频| 亚洲色成人网站WWW永久四虎| 国产精品国产午夜免费福利看| 男男黄GAY片免费网站WWW| 亚洲午夜久久久久久久久电影网| 亚洲视频在线观看免费| 亚洲一卡2卡4卡5卡6卡在线99| 成人免费网站在线观看| 美女一级毛片免费观看| 亚洲精品无码鲁网中文电影| 久久精品无码专区免费东京热 | 国产精品亚洲片在线观看不卡 | 久久久久亚洲Av片无码v| 91免费人成网站在线观看18| 亚洲Av永久无码精品一区二区| 亚洲最大av无码网址| 久久精品毛片免费观看| 亚洲精品日韩一区二区小说| 亚洲午夜av影院| 97人妻无码一区二区精品免费| 黄色免费在线网址| 久久久亚洲AV波多野结衣 | 天天拍拍天天爽免费视频| sihu国产精品永久免费| 亚洲精品不卡视频| 中文字幕亚洲日本岛国片|