1. 前言

在提倡微服务及 Serverless 越来越普及的当下,传统 .Net 应用的配置模式往往依赖于一个名为 web.config 的 XML 文件,在可扩展性和可读性与时代脱节了。当然,我不会怂恿一下子把所有应用迁移到 .Net Core 上,本文将在尽量不引入 .Net Core 开发模式的前提下,获得最大的利益。

在开始之前,我们还是先说说 .Net Core 的配置模式有何优势以及最少的依赖。

1.1 .Net Core 配置模式的优势

  • 支持多种格式,如 Json、ini、Yaml、系统环境变量等
  • 不再依赖于 web.config ,可同时使用多种配置格式
  • 支持热重载配置,修改配置可以不用重启应用

1.2 最少依赖

nuget install  Microsoft.Extensions.Configuration
nuget install Microsoft.Extensions.Configuration.Binder
nuget install Castle.Windsor (其他 IOC 框架均可)

如果你安装的是最新的包,可能会遇到 Microsoft.Extensions.Configuration 系列 Nuget 包无法安装的问题,这主要取决当前应用的 .Net 版本,请参考下图,安装对应的版本(目前不支持 .Net 4.5 以下的应用)。

由于本人喜欢可读性高的 Json 文件,所以还安装 Microsoft.Extensions.Configuration.Json 的 Nuget 包。

2. 示例教程

本文在 Abp 2.1.3 的基础上实现 .Net Core 的配置模式以及热重载配置,更详细的过程可参照我在 Github 上的 提交历史

2.1 加载配置

.Net Core 配置模式的核心是一个名为 IConfigurationRoot 的接口对象,需要在应用入口中加载各种配置格式后创建一个 IConfigurationRoot 的实例,在传统的 .Net Web 应用中是在 Global.asax.cs 中赋值。

// ConfigurationExtenion.AppConfiguration 为一个静态的 IConfigurationRoot 实例。
ConfigurationExtenion.AppConfiguration = new ConfigurationBuilder()
.AddJsonFile("appsetting.json", optional: false, reloadOnChange: true)
.AddJsonFile("appsetting01.json", optional: true, reloadOnChange: false)
.Build();

简单地解释一下,我们需要(在根目录中) 有一个名为 "appsetting.json" 的 Json 文件,被修改的同时会重载 ConfigurationExtenion.AppConfiguration 。与之相反的 "appsetting01.json" 则允许不存在,即使存在,被修改时不会重载配置。

2.2 读取配置节点

加载配置后,我们需要把配置读取出来,在 IConfigurationRoot 中所有配置的信息都是存在于一个个节点中,我们可以根据节点名称来获取对应的类型对象。

        /// <summary>
/// 获取节点配置
/// </summary>
/// <typeparam name="TService"></typeparam>
/// <param name="config"></param>
/// <param name="sectionName"></param>
/// <returns></returns>
internal static TService GetSectionObject<TService>(IConfigurationRoot config, String sectionName)
{
var section = config.GetSection(sectionName);
if (section == null)
{
throw new ArgumentException($" {sectionName} 未绑定,无法获取到配置节点信息!");
}
return section.Get<TService>();
}

假设我们有一个这样的 "appsetting.json" 文件:

{
"RedisConfiguration": {
"InstanceDbId": 14,
"InstanceRedisConnectionString": "127.0.0.1"
},
"MongoDbConfiguration": {
"ConnectionString": "mongodb://127.0.0.1:27017/?connectTimeoutMS=300000",
"DatatabaseName": "local"
}
}

如果我们要获取 MongoDbConfiguration 下的 ConnectionString 的值,那么我们可以这样获取:

var connectionString= GetSectionObject<String>(ConfigurationExtenion.AppConfiguration,"MongoDbConfiguration:ConnectionString");

2.3 设计配置类

在传统的 .Net 应用程序中,我们往往会使用一个静态变量去存放配置信息。而在有 IOC 的情况下,更好的方法是设计一个类来存放配置,如上面的 Json 文件我们可以设计如下两个类(在 Visual Studio 选择性黏贴 Json 会自动生成对象):

    public class RedisConfiguration
{
public int InstanceDbId { get; set; }
public string InstanceRedisConnectionString { get; set; }
} public class MongodbConfiguration
{
public string ConnectionString { get; set; }
public string DatatabaseName { get; set; }
}

2.4 注册配置

为了实现热重载配置,而不是一层不变的值,我们在 IOC 中获取配置类时,需要使用工厂方法获取。在 Windsor 中可以这么做:

        /// <summary>
/// 注册方法
/// </summary>
/// <typeparam name="TService"></typeparam>
/// <param name="ioc"></param>
/// <param name="factoryMethod"></param>
private static void Register<TService>(IIocManager ioc,
Func<TService> factoryMethod) where TService : class
{
ioc.IocContainer
.Register(
Component.For<TService>()
.UsingFactoryMethod(factoryMethod)
.LifestyleTransient() //这里的生命周期是瞬时的,单例不可以吗?
);
}

结合前面的获取配置节点,我们可以把两个静态方法组合起来创造一个新的静态方法,更方便我们使用。

        /// <summary>
/// 注册配置
/// </summary>
/// <typeparam name="TService"></typeparam>
/// <param name="ioc"></param>
/// <param name="config"></param>
/// <param name="sectionName"></param>
internal static void InitConfigService<TService>(IIocManager ioc, IConfigurationRoot config, String sectionName) where TService : class
{
Register(ioc, () =>
{
var service = GetSectionObject<TService>(config, sectionName);
return service;
});
}

对于前面两个配置类,我们可以这样注入:

   ConfigurationExtenion.InitConfigService<RedisConfiguration>(IocManager,ConfigurationExtenion.AppConfiguration, "RedisConfiguration");
ConfigurationExtenion.InitConfigService<MongodbConfiguration>(IocManager, ConfigurationExtenion.AppConfiguration, "MongoDbConfiguration");

2.5 获取配置

我在这里新增一个控制器方法获取 RedisConfiguration 对象,该方法使用属性注入获取指定的配置类,并返回给页面。

       public String GetRedisConfig()
{
var redisConfig = IocManager.Instance.Resolve<RedisConfiguration>();
return redisConfig.ToJsonString();
}

可以看到页面显示的与 Json 文件中结构一模一:

2.6 热重载配置

与 .Net Core 不一样,在 .Net 4.X 的 Web 应用中只是稍微修改下 Json 文件都会让整个应用重启(修改 web.config 同样会重启),所以我将 "appsetting.json" 重命名为 "appsetting.conf" ,在运行时修改某些值,并重新访问控制器,可以看到对应的值变了。

需要注意 IIS 的安全配置,Json 或者 ini 也许能直接通过 HTTP 获取!

3. 结语

通过简单的使用 .Net Core 配置模式,我们可以感受到其强大魅力,如果你对 .Net Core 更多的了解,以及感受两者的对比,可以对照我之前写过的一篇文章。对于热重载配置,.Net Core 中更多是使使用IOptionsSnapshot,而为了尽量少地引入 .Net Core 的特性,在这里只是简单地用了 IOC 的特性。当然,在不使用任何 IOC 的情况下(如果你害怕引入 IOC ),定义一个 IConfigurationRoot 的全局静态实例,也不失为一个折中的方案。

.Net 4.X 提前用上 .Net Core 的配置模式以及热重载配置的更多相关文章

  1. Net Core 的配置模式以及热重载配置

    https://www.cnblogs.com/Leo_wl/p/8527535.html https://blog.csdn.net/ma_jiang/article/details/5350198 ...

  2. 【无私分享:ASP.NET CORE 项目实战(第十章)】发布项目到 Linux 上运行 Core 项目

    目录索引 [无私分享:ASP.NET CORE 项目实战]目录索引 简介 ASP.Net Core 给我们带来的最大的亮点就是跨平台,我在我电脑(win7)上用虚拟机建了个 CentOS7 ,来演示下 ...

  3. 发布项目到 Linux 上运行 Core 项目

    发布项目到 Linux 上运行 Core 项目 目录索引 [无私分享:ASP.NET CORE 项目实战]目录索引 简介 ASP.Net Core 给我们带来的最大的亮点就是跨平台,我在我电脑(win ...

  4. Visual Studio 2017 通过SSH 调试Linux 上.NET Core

    Visual Studio 2017 通过SSH 调试Linux 上.NET Core 应用程序. 本文环境 开发环境:Win10 x64 Visual Studio 2017 部署环境:Ubuntu ...

  5. VS2017 Linux 上.NET Core调试

    调试Linux 上.NET Core Visual Studio 2017 通过SSH 调试Linux 上.NET Core 应用程序. 本文环境 开发环境:Win10 x64 Visual Stud ...

  6. ASP.NET Core的配置(5):配置的同步[设计篇]

    本节所谓的"配置同步"主要体现在两个方面:其一,如何监控配置源并在其变化的时候自动加载其数据,其目的是让应用中通过Configuration对象承载的配置与配置源的数据同步:其二. ...

  7. ASP.NET Core的配置(2):配置模型详解

    在上面一章我们以实例演示的方式介绍了几种读取配置的几种方式,其中涉及到三个重要的对象,它们分别是承载结构化配置信息的Configuration,提供原始配置源数据的ConfigurationProvi ...

  8. ASP.NET Core MVC 2.x 全面教程_ASP.NET Core MVC 02. Web Host 的默认配置

    视频地址: https://www.bilibili.com/video/av38392956/?p=2 语雀 https://www.yuque.com/yuejiangliu/dotnet/ixt ...

  9. [ASP.NET Core 3框架揭秘] 配置[7]:多样化的配置源[中篇]

    物理文件是我们最常用到的原始配置载体,而最佳的配置文件格式主要有三种,它们分别是JSON.XML和INI,对应的配置源类型分别是JsonConfigurationSource.XmlConfigura ...

随机推荐

  1. ffmpeg命令行循环推流

    用ffmpeg循环推一个文件到rtmp服务器.一般都是建议用-stream_loop选项.如: ffmpeg -threads -re -fflags +genpts -stream_loop - - ...

  2. SpringMVC源码情操陶冶-AbstractHandlerMethodMapping

    承接前文SpringMVC源码情操陶冶-AbstractHandlerMapping,本文将介绍如何注册HandlerMethod对象作为handler 类结构瞧一瞧 public abstract ...

  3. System.nanoTime

    System.currentTimeMillis()返回的毫秒,这个毫秒其实就是自1970年1月1日0时起的毫秒数. System.nanoTime()返回的是纳秒,nanoTime而返回的可能是任意 ...

  4. CF 246E. Blood Cousins Return [dsu on tree STL]

    题意: 一个森林,求k级后代中多少种不同的权值 用set维护每个深度出现的权值 一开始一直在想删除怎么办,后来发现因为当前全局维护的东西里都是当前子树里的,如果要删除那么当前一定是轻儿子,直接清空se ...

  5. 夏令营提高班上午上机测试 Day 1 解题报告

    Day 1的题难度上来说不算太高,但是T2和T3还是有一定的思维量的. 一个比较好的开始.虽然AK的人只有几个.. (懒得去翻result了..忘了当时拿了多少分了 (哦,前两天我们机房是没有成绩的, ...

  6. BZOJ 3932: [CQOI2015]任务查询系统 [主席树]

    传送门 题意: 任务用三元组(Si,Ei,Pi)描述,(Si,Ei,Pi)表示任务从第Si秒开始,在第Ei秒后结束(第Si秒和Ei秒任务也在运行),其优先级为Pi 调度系统会经常向查询系统询问,第Xi ...

  7. CF 235C. Cyclical Quest [后缀自动机]

    题意:给一个主串和多个询问串,求询问串的所有样子不同的周期同构出现次数和 没有周期同构很简单就是询问串出现次数,|Right| 有了周期同构,就是所有循环,把询问串复制一遍贴到后面啊!思想和POJ15 ...

  8. Nodejs的运行原理-函数回调篇

    前言 当客户端向http server 发起TCP链接时,server端会发起一系列的callback调用,这是一个逆向调用的过程:开始于libuv,终止于js代码里的callback(promise ...

  9. IT连创业系列:年终回顾录!

    一年不过一转眼,一光阴的青春又逝去了! 还有不到几天,就要进入新年快乐的祝福包围圈了. 在这归家之际,留文一篇,为这忙碌的一年创业留点回忆! IT连创业这一年走来: 大大小小的深坑,小小大大的困难,一 ...

  10. Netty(一):入门篇

    匠心零度 转载请注明原创出处,谢谢! 说在前面 上篇文章对Netty进行了初探:Netty初探,主要介绍了下我们为什么需要学习netty.netty介绍等:本篇文章接着上篇文章的内容.本篇为了方便大家 ...