宝哥软件园

浅析 净核心中数据配置的自动更新

编辑:宝哥软件园 来源:互联网 时间:2021-08-25

在…之前

很早在看耶西的Asp.net核心快速入门的课程的时候就了解到了在Asp .净芯中,如果添加的数据配置被更改了,是支持自动重载配置的,作为一名有着严重'造轮子'情节的程序员,最近在折腾一个博客系统,也想造出一个这样能自动更新以关系型数据库为数据源的配置来源,于是点开了AddJsonFile这个拓展函数的源码,发现别有洞天,蛮有意思,本篇文章就简单地聊一聊数据配置的重新加载更改是如何实现的,在学习重新加载更改的过程中,我们会把配置也顺带撩一把:格林:希望对小伙伴们有所帮助。

公共静态IWebHostBuilder CreateWebHostBuilder(字符串[]参数)=WebHost .CreateDefaultBuilder(参数).配置配置(选项={选项.AddJsonFile('appsettings.json ',optional:true,ReLoadOnChange : true);}) .UseStartupStartup();在Asp .净芯中如果配置了数据数据源,把重新加载更改属性设置为真实的即可实现当文件变更时自动更新配置,这篇博客我们首先从它的源码简单看一下,看完你可能还是会有点懵的,别慌,我会对这些代码进行精简,做个简单的小例子,希望能对你有所帮助。

一窥源码

AddJson

首先,我们当然是从这个我们耳熟能详的扩展函数开始,它经历的演变过程如下。

公共静态iconfigulationbuilder AddJsonFile(此图标构建器构建器,字符串路径布尔可选,bool ReLoadOnChange){ return builder .AddJsonFile((IFileProvider)null,路径,可选,ReLoadOnChange);}传递一个空的文件提供程序给另外一个重载Addjson函数。

敲黑板,空的文件提供程序很重要,后面要考:英里:英里。

公共静态iconfigulationbuilder AddJsonFile(此图标配置生成器生成器,IFileProvider提供程序,字符串路径布尔可选,bool ReLoadOnChange){ return builder .AddJsonFile((ActionJsonConfigurationSource)(s={ s . file provider=provider;路径=路径;可选=可选;s . ReLoadOnChange=ReLoadOnChange;s . ResolveFileProvider();}));}把传入的参数演变成一个行动委托给JsonConfigurationSource的属性赋值。

公共静态iconfigulationbuilder AddJsonFile(此IConfigurationBuilder builder,ActionJsonConfigurationSource configureSource){ return builder .AddJsonConfigurationSource(configureSource);}最终调用的builder.add(操作)方法。

公共静态iconfigulationbuilder添加资源(此图标配置生成器生成器,操作资源配置源)其中t source : iconfigulationsource,new(){ t source=new t source();if (configureSource!=null)配置来源(源);返回生成器。添加((图标配置源)源);}在增加方法里,创建了一个来源实例,也就是JsonConfigurationSource实例,然后把这个实例传为刚刚的委托,这样一来,我们在最外面传入的appsettings.json ',optional:true,reloadOnChange:true参数就作用到这个示例上了。

最终,这个实例添加到建设者中。那么建设者又是什么?它能干什么?

配置构建

前面提及的建设者默认情况下是配置生成器,我对它的进行了简化,关键代码如下。

公共类配置生成器:图标配置生成器{公共ilisticonfigurationsources { get;}=new listiticonfigutionsource();公共图标配置生成器添加(图标配置源){来源.添加(来源);归还这个;} public iconfigulationroot Build(){ var providers=new listiticonfigulationprovider();foreach(源中的var source){ var provider=source .建造(这个);提供商添加(提供者);}返回新的ConfigurationRoot(提供程序);} }可以看到,这个建设者中有个集合类型的来源,这个来源可以保存任何实现了图标配置源的来源,前面聊到的JsonConfigurationSource就是实现了这个接口,常用的还有内存配置源,XmlConfigureSource,CommandLineConfigurationSource等。

另外,它有一个很重要的建设方法,这个建设方法在WebHostBuilder方法执行建设的时候也被调用,不要问我WebHostBuilder.builder方法什么执行的快乐.

公共静态void Main(字符串[]参数){ CreateWebHostBuilder(参数)。构建()。run();}在配置生成器的方法里面就调用了每个来源的建设者方法,我们刚刚传入的是一个JsonConfigurationSource,所以我们有必要看看JsonSource的建设者做了什么。

这里是不是被这些建设者绕哭了?别慌,下一篇文章中我会讲解如何自定义一个配置资源,会把外形系列类用户模式类图整理一下,应该会清晰很多。

JsonConfigurationSource

公共类JsonConfigurationSource :文件配置source { public override iconfigulationprovider Build(iconfigulationbuilder builder){ EnsureDefaults(builder);返回新的JsonConfigurationProvider(this);} }这就是JsonConfigurationSource的所有代码,未精简,它只实现了一个建设方法,在建设内,确保默认值被调用,可别小看它,之前那个空的文件提供程序在这里被赋值了。

public void EnsureDefaults(iconfigulationbuilder builder){ file provider=file provider?建筑商getfile provider();}公共静态IFileProvider GetFileProvider(此图标配置生成器生成器){返回新的physicalfilepider(AppContext .BaseDirectory?字符串。空的);}可以看到这个文件提供程序默认情况下就是物理文件提供程序,为什么对这个文件提供程序如此宠幸让我花如此大的伏笔要强调它呢?往下看。

JsonConfigurationProvider文件配置提供程序

在JsonConfigurationSource的建设方法内,返回的是一个JsonConfigurationProvider实例,所以直觉告诉我,在它的构造函数内必有猫腻困惑的:

公共类JsonConfigurationProvider :文件配置提供程序{ public JsonConfigurationProvider(JsonConfigurationSource): base(source){ public override void Load(Stream)Stream){ try { Data=jsonconfigurationfile parser .解析(流);} catch(jsonreadexception e){ 0抛出新的FormatException(参考资料Error_JSONParseError,e);} } }看不出什么的代码,事出反常必有妖~~

看看基础的构造函数。

公共文件配置提供程序(文件配置源源){ Source=sourceif(来源。重新加载更改源文件提供者!=null){ _ changeTokenRegistration=ChangeToken .OnChange(()=来源.文件提供者。观察(来源。路径),()={ Thread .睡眠(来源。重新加载延迟);加载(重加载:真);});} }真是个天才,问题就在这个构造函数里,它构造函数调用了一个ChangeToken .待清扫房方法,这是实现重新加载更改的关键,如果你点到这里还没有关掉,恭喜,好戏开始了。

重新加载更改

空谈是廉价的。给我看看代码(屁话少说,放码过来).

公共静态类ChangeToken {公共静态change token registration Action OnChange(funichangetoken changeTokenProducer,Action changeTokenConsumer) {返回新的changetokenregistration Action(changeTokenProducer,callback=callback()),changeTokenConsumer);}}在onchange方法中,不考虑func func、action,只看这两个参数的名称,生产者、消费者、生产者和消费者。看到这个关键词不知道想到了什么。不管怎样,我想起了小学学习食物链时的:snake:和:rat:

然后我们看看:snake:是什么,rat:是什么,我们要回到FileConfigurationProvider的构造函数。

可以看出制作人:rat:是:

()=source . file provider . watch(source . path)消费者:snake:是:

()={ Thread。睡眠(来源。重新加载延迟);加载(re load : true);}让我们考虑一下。一旦:rat:出来,就会被:snake:吃掉。

那么我们在这里是一样的。一旦文件提供程序返回了一些东西。注意,将发生Load()事件来重新加载数据。

:snake:和:rat:很容易理解,但是代码没那么容易理解。从OnChange的第一个参数FuncIChangeToken change token producer方法中我们知道,这里的:而不是:实际上是ichangetoken。

IChangeToken

公共接口IChangeToken { bool HasChanged { get;} boolactivechange回调{ get} IDisposable RegisterChangeCallback(Actionobject回调,对象状态);}IChangeToken的关键点在于它有一个RegisterChangeCallback方法, snake : eat : rat :的事实就发生在这个回调方法中。

让我们做一个吃:纳克的实验。拉特:拉特:

实验1

Static void Main() {//文件提供程序var phyfilepider=new physicalfilepider(' c : users Liz my box test space ')定义c: users liuzh的目录;//让此提供程序开始侦听此目录中的所有文件:varchangetoken=phyfile Provider . watch(' *)。*');//注册吃这个东西给回调函数change token . registerchangecallback(_={ console . writeline('鼠标被蛇吃');},new object());//将文件添加到目录AddFileToPath();控制台。ReadKey();} static void addfile topath(){ console。writeline('鼠标出洞');文件。create(' c : user Liu my box test space 鼠标出洞。txt ')。dispose();}这是操作的结果

可以看到,一旦在监听目录中创建了文件,就会立即触发回调函数,但是如果我们继续手动更改(复制)监听目录中的文件,回调函数将不再执行。

这是因为在changetoken监视文件更改并触发回调函数后,changetoken的任务就完成了。如果我们想一直保持监控,我们会在回调函数中重新获取Token,并为新Token的回调函数注册通用事件,这样我们就可以一直保持监控。

这就是ChangeToken。Onchange有。让我们看看源代码。

公共静态类ChangeToken {公共静态更改令牌注册操作OnChange(funinchangetoken changeTokenProducer,Action changeTokenConsumer){ 0返回新的changetokenregistration Action(changeTokenProducer,callback=callback()),changeTokenConsumer);} }公共类changetokenregistrationtacticon { private readonly Funcichangetoken _ changeTokenProducer;私有只读操作tacticon _ changeTokenConsumer私有只读tacticon _ state public change token registration(funinchange token change token producer,action tacticon change token consumer,tacticon state){ _ change token producer=change token producer;_ changeTokenConsumer=changeTokenConsumer;_ state=state var token=changeTokenProducer();RegisterChangeTokenCallback(令牌);} private void RegisterChangeTokenCallback(IChangeToken令牌){ token .RegisterChangeCallback(_=onchangetkenfried(),this);} private void OnChangeTokenFired(){ var token=_ changeTokenProducer();尝试{ _ changeTokenConsumer(_ state);}最后{ //我们始终希望确保回调已注册RegisterChangeTokenCallback(令牌);} } }简单来说,就是给代币注册了一个OnChangeTokenFired的回调函数,仔细看看OnChangeTokenFired里做了什么,总体来说三步。

1.获取一个新的令牌. 2 .调用消费者进行消费.3.给新获取的代币再次注册一个OnChangeTokenFired的回调函数。

如此周而复始~~

实验2

既然知道了待清扫房的工作方式,那么我们把实验一的代码修改一下。

static void Main(){ var phyfilepider=new physicalfilepider(' c : Users Liz MyBox TestSpace ');ChangeToken .OnChange(()=phyFileProvider .手表(' *。*),()={控制台WriteLine(“”老鼠被蛇吃');});控制台ReadKey();}执行效果看一下

可以看到,只要被监控的目录发生了文件变化,不管是新建文件,还是修改了文件内的内容,都会触发回调函数,其实JsonConfig中,这个回调函数就是Load(),它负责重新加载数据,可也就是为什么Asp .净芯中如果把重新加载更改设置为真实的后,Json的配置一旦更新,配置就会自动重载。

PhysicalFilesWatcher

那么,为什么文件一旦变化,就会触发ChangeToken的回调函数呢?其实物理文件提供程序中调用了PhysicalFilesWatcher对文件系统进行监视,观察PhysicalFilesWatcher的构造函数,可以看到PhysicalFilesWatcher需要传入文件系统监视器是system.io下的底层超正析象管(图像或图标)类,在构造函数中给这个看守人的创建、更改、重命名、删除注册事件处理程序事件,最终,在这些事件处理程序中会调用长通肯的回调函数,所以文件系统一旦发生变更就会触发回调函数。

public PhysicalFilesWatcher(字符串根,文件系统监视器,文件系统监视器,bool pollForChanges,ExclusionFilters筛选器){这个._ root=root这个_文件观察器=文件系统观察器;这个文件观察者.IncludeSubdirectories=true这个文件观察者.已创建=新文件系统事件处理程序(这. OnChanged);这个文件观察者.已更改=新文件系统事件处理程序(这. OnChanged);这个文件观察者.重命名=新的RenamedEventHandler(这. OnRenamed);这个文件观察者.已删除=新文件系统事件处理程序(这. OnChanged);这个文件观察者.错误=新的ErrorEventHandler(这. OnError);这个PollForChanges=pollForChanges这个_filters=筛选器;这个轮询更改令牌=new concurrentdictionary轮询更改令牌,IPollingChangeToken();这个_ TimerFactory=(FuncTimer)(()=nonCapturingTimer .创建(新的)TimerCallback(PhysicalFilesWatcher .RaiseChangeEvents),(对象)这个。轮询更改令牌,时间跨度。零,PhysicalFilesWatcher .default polling interval));}如果你和我一样,对源码感兴趣,可以从官方的aspnet/扩展中下载源码研究: https://github.com/aspnet/Extensions

在下一篇文章中,我将解释如何定制一个以Mysql为数据源的ConfigureSoure,并实现自动更新功能。同时,我将组织配置相关类的UML类图。感兴趣的人可以关注我,以便第一时间收到下一篇文章。

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

以上就是本文的全部内容。希望对大家的学习有帮助,支持我们。

更多资讯
游戏推荐
更多+