欧美大屁股bbbbxxxx,狼人大香伊蕉国产www亚洲,男ji大巴进入女人的视频小说,男人把ji大巴放进女人免费视频,免费情侣作爱视频

歡迎來到入門教程網(wǎng)!

ASP.NET

當(dāng)前位置:主頁 > 網(wǎng)絡(luò)編程 > ASP.NET >

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

來源:本站原創(chuàng)|時間:2020-01-11|欄目:ASP.NET|點(diǎn)擊: 次

Pre

很早在看 Jesse 的 Asp.net Core快速入門 的課程的時候就了解到了在Asp .net core中,如果添加的Json配置被更改了,是支持自動重載配置的,作為一名有著嚴(yán)重"造輪子"情節(jié)的程序員,最近在折騰一個博客系統(tǒng),也想造出一個這樣能自動更新以Mysql為數(shù)據(jù)源的ConfigureSource,于是點(diǎn)開了AddJsonFile這個拓展函數(shù)的源碼,發(fā)現(xiàn)別有洞天,蠻有意思,本篇文章就簡單地聊一聊Json config的ReloadOnChange是如何實現(xiàn)的,在學(xué)習(xí)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數(shù)據(jù)源,把reloadOnChange屬性設(shè)置為true即可實現(xiàn)當(dāng)文件變更時自動更新配置,這篇博客我們首先從它的源碼簡單看一下,看完你可能還是會有點(diǎn)懵的,別慌,我會對這些代碼進(jìn)行精簡,做個簡單的小例子,希望能對你有所幫助.

一窺源碼

AddJson

首先,我們當(dāng)然是從這個我們耳熟能詳?shù)臄U(kuò)展函數(shù)開始,它經(jīng)歷的演變過程如下.

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

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

敲黑板,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();
  }));
 }

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

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

最終調(diào)用的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方法里,創(chuàng)建了一個Source實例,也就是JsonConfigurationSource實例,然后把這個實例傳為剛剛的委托,這樣一來,我們在最外面?zhèn)魅氲?"appsettings.json",optional:true,reloadOnChange:true 參數(shù)就作用到這個示例上了.

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

ConfigurationBuild

前面提及的builder默認(rèn)情況下是 ConfigurationBuilder ,我對它的進(jìn)行了簡化,關(guān)鍵代碼如下.

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可以保存任何實現(xiàn)了 IConfigurationSource 的Source,前面聊到的 JsonConfigurationSource 就是實現(xiàn)了這個接口,常用的還有 MemoryConfigurationSource , XmlConfigureSource , CommandLineConfigurationSource 等.

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

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

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

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

JsonConfigurationSource

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

這就是 JsonConfigurationSource 的所有代碼,未精簡,它只實現(xiàn)了一個Build方法,在Build內(nèi),EnsureDefaults被調(diào)用,可別小看它,之前那個空的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默認(rèn)情況下就是 PhysicalFileProvider ,為什么對這個 FileProvider 如此寵幸讓我花如此大的伏筆要強(qiáng)調(diào)它呢?往下看.

JsonConfigurationProvider && FileConfigurationProvider

在JsonConfigurationSource的build方法內(nèi),返回的是一個JsonConfigurationProvider實例,所以直覺告訴我,在它的構(gòu)造函數(shù)內(nèi)必有貓膩: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的構(gòu)造函數(shù).

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

真是個天才,問題就在這個構(gòu)造函數(shù)里,它構(gòu)造函數(shù)調(diào)用了一個 ChangeToken.OnChange 方法,這是實現(xiàn)ReloadOnChange的關(guān)鍵,如果你點(diǎn)到這里還沒有關(guān)掉,恭喜,好戲開始了.

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,就看看這兩個參數(shù)的名稱,producer,consumer,生產(chǎn)者,消費(fèi)者,不知道看到這個關(guān)鍵詞想到的是什么,反正我想到的是小學(xué)時學(xué)習(xí)食物鏈時的:snake:與:rat:.

那么我們來看看這里的:snake:是什么,:rat:又是什么,還得回到 FileConfigurationProvider 的構(gòu)造函數(shù).

可以看到生產(chǎn)者:rat:是:

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

消費(fèi)者:snake:是:

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

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

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

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

IChangeToken

 public interface IChangeToken
 {
  bool HasChanged { get; }

  bool ActiveChangeCallbacks { get; }

  IDisposable RegisterChangeCallback(Action<object> callback, object state);
 }

IChangeToken的重點(diǎn)在于里面有個RegisterChangeCallback方法,:snake:吃:rat:的這件事,就發(fā)生在這回調(diào)方法里面.

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

實驗1

 static void Main()
  {
   //定義一個C:\Users\liuzh\MyBox\TestSpace目錄的FileProvider
   var phyFileProvider = new PhysicalFileProvider("C:\\Users\\liuzh\\MyBox\\TestSpace");

   //讓這個Provider開始監(jiān)聽這個目錄下的所有文件
   var changeToken = phyFileProvider.Watch("*.*");

   //注冊🐍吃🐀這件事到回調(diào)函數(shù)
   changeToken.RegisterChangeCallback(_=> { Console.WriteLine("老鼠被蛇吃"); }, new object());

   //添加一個文件到目錄
   AddFileToPath();

   Console.ReadKey();

  }

  static void AddFileToPath()
  {
   Console.WriteLine("老鼠出洞了");
   File.Create("C:\\Users\\liuzh\\MyBox\\TestSpace\\老鼠出洞了.txt").Dispose();
  }

這是運(yùn)行結(jié)果

可以看到,一旦在監(jiān)聽的目錄下創(chuàng)建文件,立即觸發(fā)了執(zhí)行回調(diào)函數(shù),但是如果我們繼續(xù)手動地更改(復(fù)制)監(jiān)聽目錄中的文件,回調(diào)函數(shù)就不再執(zhí)行了.

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

這也就是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 的回調(diào)函數(shù),仔細(xì)看看 OnChangeTokenFired 里做了什么,總體來說三步.

1.獲取一個新的token.
2.調(diào)用消費(fèi)者進(jìn)行消費(fèi).
3.給新獲取的token再次注冊一個OnChangeTokenFired的回調(diào)函數(shù).

如此周而復(fù)始~~

實驗2

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

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

執(zhí)行效果看一下

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

PhysicalFilesWatcher

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

 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為數(shù)據(jù)源的ConfigureSoure,并實現(xiàn)自動更新功能,同時還會整理Configure相關(guān)類的UML類圖,有興趣的可以關(guān)注我以便第一時間收到下篇文章.

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

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持我們。

上一篇:.net core高吞吐遠(yuǎn)程方法如何調(diào)用組件XRPC詳解

欄    目:ASP.NET

下一篇:如何給asp.net core寫個簡單的健康檢查

本文標(biāo)題:淺析.Net Core中Json配置的自動更新

本文地址:http://mengdiqiu.com.cn/a1/ASP_NET/10933.html

網(wǎng)頁制作CMS教程網(wǎng)絡(luò)編程軟件編程腳本語言數(shù)據(jù)庫服務(wù)器

如果侵犯了您的權(quán)利,請與我們聯(lián)系,我們將在24小時內(nèi)進(jìn)行處理、任何非本站因素導(dǎo)致的法律后果,本站均不負(fù)任何責(zé)任。

聯(lián)系QQ:835971066 | 郵箱:835971066#qq.com(#換成@)

Copyright © 2002-2020 腳本教程網(wǎng) 版權(quán)所有