Dotnet 6.0 已来。

Dotnet 6.0 大家都装了没?

我打算开个专题,系统地写一写 Dotnet 6.0 在各个方面的特性,以及全新的开发方式。也是因为最近讨论 6.0 比较多,看到很多人的畏难情绪,所以打算写写相关的内容。

了解了,就不怕了。

要写的内容很多,我会分几篇来写。

今天是第一篇:ConfigurationManager,配置管理器。

ConfigurationManager 是干什么用的?

引用微软官方的说法:ConfigurationManager 是用来支持 ASP.Net Core 的新的 WebApplication 模型。这个模型主要的作用是在一些特定的场景下(后面我们会说到),用来简化 ASP.NET Core 的启动代码。

当然,如果我们去看 MSDN 的文档,会发现 ConfigurationManager 本身实现还是挺复杂的。好在,大多数情况下,这是一个半隐藏的东西,你可能都意识不到你已经用到了它。

那它到底是干什么用的?

这得从 .Net 5.0 的 Configuration 说起。

.Net 5.0 里的 Configuration

Configuration 配置,从 3.1 到 5.0,增加了很多很多的配置类型,如果你去 MSDN 上看,有好几大篇。

这里面,我们接触最多的是两个:

  • IConfigurationBuilder - 这个接口主要用来增加配置源,并在构建器上调用 Build() 来读取每个配置源,并形成最终的配置

  • IConfigurationRoot - 这就是上面 Build() 完成后形成的配置,我们会从这里面读配置值

在实际应用中,IConfigurationBuilder 通常被我们用做配置源列表的包装器,最常用的是通过 AddJsonFile(),将配置源添加到源列表中。看到 AddJsonFile(),你是不是想到了什么?

简单来说,IConfigurationBuilder 是这样的:

public interface IConfigurationBuilder
{IDictionary<string, object> Properties { get; }IList<IConfigurationSource> Sources { get; }IConfigurationBuilder Add(IConfigurationSource source);IConfigurationRoot Build();
}

而 IConfigurationRoot,里面放的是经过合并的配置值。这个合并需要注意一下,多个配置源逐个加入时,相同名称的项,后面的配置会覆盖前面的项。

在 .Net 5.0 以前,IConfigurationBuilder 和 IConfigurationRoot 接口分别由 ConfigurationBuilder 和 ConfigurationRoot 实现。使用时通常是这么写:

var builder = new ConfigurationBuilder();// 加入静态值
builder.AddInMemoryCollection(new Dictionary<string, string>
{{ "MyKey", "MyValue" },
});// 加入文件
builder.AddJsonFile("appsettings.json");IConfigurationRoot config = builder.Build();string value = config["MyKey"]; // 取一个值
IConfigurationSection section = config.GetSection("SubSection"); // 取一个节

这是在 Console 程序中。

在 ASP.NET Core 中,通常不需要这么显式的 new 和 Build(),但事实上也是调用的这个接口。

在默认的 ConfigurationBuilder 实现中,调用 Build() 将遍历所有的源,加载 Provider 程序,并将它们传递给一个新的ConfigurationRoot 实例:

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

然后,ConfigurationRoot 依次遍历每个提供程序并加载配置值:

public class ConfigurationRoot : IConfigurationRoot, IDisposable
{private readonly IList<IConfigurationProvider> _providers;private readonly IList<IDisposable> _changeTokenRegistrations;public ConfigurationRoot(IList<IConfigurationProvider> providers){_providers = providers;_changeTokenRegistrations = new List<IDisposable>(providers.Count);foreach (IConfigurationProvider p in providers){p.Load();_changeTokenRegistrations.Add(ChangeToken.OnChange(() => p.GetReloadToken(), () => RaiseChanged()));}}// ...
}

这种架构,会有个小问题。在团队开发的时候,在没有统一沟通的情况下,有可能会在多处调用 Build()。当然这也没什么问题。只不过,正常来说这个没有必要,毕竟这是在读文件,会很慢。

不过,在 .Net 5.0 之前,都是这么做。

可喜的是,在 .Net 6.0 里,微软也注意到这个问题,并引入了一个新的类型:ConfigurationManager。

.Net 6.0 里的 ConfigurationManager

ConfigurationManager 是一个 .Net 6.0 中新的配置类型。这个类型也同样实现了两个接口:IConfigurationBuilder 和 IConfigurationRoot。那么,通过这两个接口的实现,我们可以简化上一节讲到的 .Net 5.0 中的通用模式。

不过,还是有一点点区别。这里 IConfigurationBuilder 将源保存为 IList:

public interface IConfigurationBuilder
{IList<IConfigurationSource> Sources { get; }// ...
}

这样做有一个好处,就是对于源 IList,就有了 Add() 和 Remove() 方法,我们可以在不知道 ConfigurationManager 的情况下增加和删除配置提供程序。

private class ConfigurationSources : IList<IConfigurationSource>
{private readonly List<IConfigurationSource> _sources = new();private readonly ConfigurationManager _config;public ConfigurationSources(ConfigurationManager config){_config = config;}public void Add(IConfigurationSource source){_sources.Add(source);_config.AddSource(source); // 增加源}public bool Remove(IConfigurationSource source){var removed = _sources.Remove(source); // 删除源_config.ReloadSources(); // 重新加载源return removed;}// ...
}

这样做可以确保 ConfigurationManager 在改变源的 IList 时,能自动加载源的配置数据。

看一下 ConfigurationManager.AddSource 的定义:

public class ConfigurationManager
{private void AddSource(IConfigurationSource source){lock (_providerLock){IConfigurationProvider provider = source.Build(this);_providers.Add(provider);provider.Load();_changeTokenRegistrations.Add(ChangeToken.OnChange(() => provider.GetReloadToken(), () => RaiseChanged()));}RaiseChanged();}
}

这个方法会立即调用 IConfigurationSource 的 Build() 方法来创建 IConfigurationProvider,并加入到源列表中。

下面,这个方法就调用 IConfigurationProvider 的 Load() 方法,将数据加载到 Provider。

这个方法解决了一件事,就是当我们需要从不同的位置向 IConfigurationBuilder 加入各种源时,源只需要加载一次,而且只会加载一次。

上面的代码是增加源。当我们需要 Remove() 源,或者干脆清除掉全部的源 Clear() 时,就需要调用 ReloadSource():

private void ReloadSources()
{lock (_providerLock){DisposeRegistrationsAndProvidersUnsynchronized();_changeTokenRegistrations.Clear();_providers.Clear();foreach (var source in _sources){_providers.Add(source.Build(this));}foreach (var p in _providers){p.Load();_changeTokenRegistrations.Add(ChangeToken.OnChange(() => p.GetReloadToken(), () => RaiseChanged()));}}RaiseChanged();
}

当然,看懂上面的代码,也就明白两件事:

  1. 增加源是代码最小的,加到列表中就行了 ;

  2. 删除或更改源代码会有点大,需要重新遍历加载所有源。

如果需要对配置的源进行大量的操作,这样的代价会比较大。不过,这种情况会很不常见。

总结一下

.Net 6.0 引入了一个新的 ConfigurationManager,用来优化配置的构建。

ConfigurationManager 同样实现了 ConfigurationBuilder 和 ConfigurationRoot。这算是个兼容性的设置,主要是为了支持 WebHostBuilder 和 HostBuilder 中对配置的调用。同时,也兼容了早期代码中的调用方式。所以,代码升级时,相关配置调用的部分,如果不想改代码,是完全可以的。而如果想做点改动,就换成使用 ConfigurationManager,或者通过 WebApplicationBuilder 来加载(会自动调用 ConfigurationManager),应用程序会有更好的性能。

这算是一个小礼物,相信也是微软权衡以后的结果。

又:写着写着,发出这个题目有点大了,要写的很多,可最近年底了,又有点忙,得容我慢慢写:P

喜欢就来个三连,让更多人因你而受益

Dotnet 6.0 深度探索(一)相关推荐

  1. 深度探索Hyperledger技术与应用之超级账本初体验(附部署代码)

    2019独角兽企业重金招聘Python工程师标准>>> 本章零基础地介绍了如何快速体验超级账本搭建的区块链网络,我们先绕过了比较复杂的初始化配置,用官方提供的fabric-sampl ...

  2. 《Android深度探索(卷1):HAL与驱动开发》——1.6节 Linux设备驱动

    本节书摘来自异步社区<Android深度探索(卷1):HAL与驱动开发>一书中的第1章,第1.6节 Linux设备驱动,作者李宁,更多章节内容可以访问云栖社区"异步社区" ...

  3. 《Android深度探索(卷1):HAL与驱动开发》——6.4节使用多种方式测试Linux驱动...

    本节书摘来自异步社区<Android深度探索(卷1):HAL与驱动开发>一书中的第6章,第6.4节使用多种方式测试Linux驱动,作者李宁,更多章节内容可以访问云栖社区"异步社区 ...

  4. 《深度探索C++对象模型》--5 构造析构拷贝 6 执行期语意学

     <深度探索C++对象模型>--5构造.析构.拷贝语意学 1.纯虚函数: (1)C++可以定义和调用一个纯虚函数,不过只可以静态调用,不可以由虚拟机制调用. 注意:pure virtu ...

  5. Android深度探索第五章

    开发板是开发和学习嵌入式技术的主要硬件设备,开发板拥有许多扩展的端口,可以很容易开发定制的硬件,并与开发板链接.目前市面上的开发板型号和种类很多,但目前最流行的是基于三星S3C6410ARM11架构的 ...

  6. 深度探索C++ 对象模型(6)-Data member的存取

    nonstatic data member 需要在class object起始地址加上该member的偏移. class A {public: int x; int y;}; A a; a.y = 0 ...

  7. 深度探索C++ 对象模型(5)-Initialization list(2)

    Initialization list的作用是效率 如下代码可以编译并运行,但是效率低下 class Word {String _name;int _cnt;public:Word() { _name ...

  8. 深度探索C++ 对象模型(4)-Default Copy Constructor(4)

    编译器层NRV优化(Named Return Value) 原代码为: class testObj{friend testObj foo(double);public:testObj(){memset ...

  9. 深度探索C++ 对象模型(3)-默认构造函数Default Constructor续

    (1)带有虚函数的类 class Widget { public: virtual int flip() = 0;//..}; void flip(const Widget* widget ) { w ...

最新文章

  1. add-apt-repository命令详解_Dcr_Hs的博客-CSDN博客_add-apt-repository
  2. ViewPager实现引导界面以及进入下一个activity解决办法
  3. css线加点的进度,纯CSS实现的交互式进度条(点击带进度填充动画)
  4. window下的SCROLLbar的使用技巧
  5. 易优CMS精美简洁作文范文网站模板源码
  6. 在IE中用js改变table的innerHTML属性报“未知的运行时错误”
  7. java interface泛型_JAVA的接口泛型
  8. 使用inno setup制作安装包
  9. 医院计算机网络系统的现状,医院信息系统应用的现状与发展
  10. 关于二叉树前中后序遍历的常见问题
  11. ug二次开发python_UG/NX二次开发入门指导
  12. Ubuntu18.04-ROS-Python-通过周立功Can发送16进制整数
  13. 一建工程经济的关键数据推导及关键记忆点
  14. 最短公共超序列(最短公共父序列)
  15. 回首过去,立足当下,展望未来
  16. 基于SSM第一医院病案管理系统
  17. 基于51单片机的蓝牙计步器设计
  18. matlab中不同数据点的动态显示并生成gif图片
  19. 解决/lib/ld-linux.so.2: bad ELF interpreter: No such file or directory报错
  20. python学生成绩管理系统实验报告_【python 学生信息管理系统】

热门文章

  1. Python:file (read,readline,readline )使用方法
  2. Quartus DSE 初步应用
  3. 关于java连接sqlserver2000 和sqlserver2005的初识
  4. 18秋学期《计算机网络》在线作业,18秋北交《计算机应用基础及计算机网络与应用》在线作业一-2辅导资料.docx...
  5. MySQL日期数据类型、时间类型使用总结
  6. ElasticSearch教程——自定义分词器(转学习使用)
  7. MySQL For RedHat Linux(源码安装,附安装包)
  8. ArcGis融合小多边形到相邻多边形
  9. viper4android 生效,另一种让V4a音效在Poweramp上生效的方法
  10. 给Web开发人员的以太坊入坑指南