# 第2章：配置管理

#### 任务9：配置介绍 <a href="#ren-wu-9-pei-zhi-jie-shao" id="ren-wu-9-pei-zhi-jie-shao"></a>

* 命令行配置
* Json文件配置
* 从配置文件文本到c#对象实例的映射 - Options 与 Bind
* 配置文件热更新
* 框架设计：Configuration

#### 任务10：命令行配置 <a href="#ren-wu-10-ming-ling-hang-pei-zhi" id="ren-wu-10-ming-ling-hang-pei-zhi"></a>

新建项目CommandLineSample--控制台应用（.NET Core）

管理NuGet程序包--下载microsoft.aspnetcore.all

![](https://256343630-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Flyk5lHXG8Cnqf7q1KuaO%2Fuploads%2FRalr3j8AC90zAOUneBSw%2F022.jpg?alt=media\&token=17ce489d-c898-4541-acfc-f3e7a4b83ac9)

**传入参数**

```
using System;
using Microsoft.Extensions.Configuration;

namespace CommandLineSample
{
    class Program
    {
        static void Main(string[] args)
        {
            var builder = new ConfigurationBuilder()
                .AddCommandLine(args);

            var configuration = builder.Build();

            Console.WriteLine($"name: {configuration ["name"]}");
            Console.WriteLine($"age: {configuration["age"]}");

            Console.ReadLine();
        }
    }
}
```

项目右键--调试--输入参数：name=mingsonzheng age=18

![](https://256343630-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Flyk5lHXG8Cnqf7q1KuaO%2Fuploads%2FXv6qr5LG6xL65Z3TYKlN%2F023.jpg?alt=media\&token=2a762b54-352b-4a7c-9381-6defd3a346a6)

启动项目

![](https://256343630-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Flyk5lHXG8Cnqf7q1KuaO%2Fuploads%2FbLOMkWBAarrjCIyG3ZiZ%2F024.jpg?alt=media\&token=79248088-5ee8-480e-a92e-ad43967e9e59)

**默认参数**

```
using System;
using System.Collections.Generic;
using Microsoft.Extensions.Configuration;

namespace CommandLineSample
{
    class Program
    {
        static void Main(string[] args)
        {
            var settings = new Dictionary<string, string>
            {
                {"name", "mingsonzheng" },
                {"age", "18" }
            };

            var builder = new ConfigurationBuilder()
                .AddInMemoryCollection(settings)
                .AddCommandLine(args);

            var configuration = builder.Build();

            Console.WriteLine($"name: {configuration ["name"]}");
            Console.WriteLine($"age: {configuration["age"]}");

            Console.ReadLine();
        }
    }
}
```

清空应用程序参数

![](https://256343630-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Flyk5lHXG8Cnqf7q1KuaO%2Fuploads%2FUtZl6dIqH9CpD1vZElDm%2F025.jpg?alt=media\&token=329e5c5a-9da0-44d4-9d84-7e7638c320e9)

启动项目

![](https://256343630-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Flyk5lHXG8Cnqf7q1KuaO%2Fuploads%2FFGzdISHBDpRDT8kNWMif%2F026.jpg?alt=media\&token=14d57154-75ea-4b4f-a3bf-3b6b07ca6fba)

通过PowerShell运行程序，默认参数与传入参数对比

```
PS D:\jessetalk\CommandLineSample\CommandLineSample\bin\Debug\netcoreapp2.1> dotnet CommandLineSample.dll
name: mingsonzheng
age: 18

PS D:\jessetalk\CommandLineSample\CommandLineSample\bin\Debug\netcoreapp2.1> dotnet CommandLineSample.dll name=jim age=22
name: jim
age: 22
```

#### 任务11：Json文件配置 <a href="#ren-wu-11json-wen-jian-pei-zhi" id="ren-wu-11json-wen-jian-pei-zhi"></a>

新建项目JsonComfigSample--控制台应用（.NET Core）

管理NuGet程序包--下载microsoft.aspnetcore.all

添加Json文件：项目右键--添加新建项class.json

![](https://256343630-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Flyk5lHXG8Cnqf7q1KuaO%2Fuploads%2FSjRU5rLSnztQe2XZ8w3c%2F027.png?alt=media\&token=dc272d69-5210-46ef-a3cb-4e4b1359d85a)

```
{
  "ClassNo": "1",
  "ClassDesc": "ASP.NET Core 101",

  "Students": [
    {
      "name": "mingsonzheng",
      "age": "18"
    },
    {
      "name": "jim",
      "age": "28"
    },
    {
      "name": "tom",
      "age": "38"
    }
  ]
}
```

由于class.json不在bin\Debug目录下，所以默认不会被编译，文件右键属性，修改为始终复制

![](https://256343630-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Flyk5lHXG8Cnqf7q1KuaO%2Fuploads%2FD6pMxn21rEsOip8jGkJG%2F028.png?alt=media\&token=949f9703-a6f3-402f-ae85-74406032117d)

```
using System;
using Microsoft.Extensions.Configuration;

namespace JsonComfigSample
{
    class Program
    {
        static void Main(string[] args)
        {
            var builder = new ConfigurationBuilder()
                .AddJsonFile("class.json");

            Console.ReadLine();
        }
    }
}
```

启动项目，可以看到class.json被复制到bin\Debug目录，这样dll就可以读取到class.json文件

![](https://256343630-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Flyk5lHXG8Cnqf7q1KuaO%2Fuploads%2FBISKsduGptMLRMQ7GPbX%2F029.jpg?alt=media\&token=3adc094b-14bc-401a-801e-ac25063be715)

**读取json文件**

```
using System;
using Microsoft.Extensions.Configuration;

namespace JsonComfigSample
{
    class Program
    {
        static void Main(string[] args)
        {
            var builder = new ConfigurationBuilder()
                .AddJsonFile("class.json");

            // 调用Build之前请确保拷贝的class.json文件没有格式错误
            var configuration = builder.Build();

            Console.WriteLine($"ClassNo: { configuration["ClassNo"]}");
            Console.WriteLine($"ClassDesc: { configuration["ClassDesc"]}");

            Console.WriteLine("Students");

            Console.Write(configuration["Students:0:name"]);
            Console.WriteLine(configuration["Students:0:age"]);

            Console.Write(configuration["Students:1:name"]);
            Console.WriteLine(configuration["Students:1:age"]);

            Console.Write(configuration["Students:2:name"]);
            Console.WriteLine(configuration["Students:2:age"]);

            Console.ReadLine();
        }
    }
}
```

启动项目

![](https://256343630-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Flyk5lHXG8Cnqf7q1KuaO%2Fuploads%2FUGigH7j68ybhxUXemBys%2F030.jpg?alt=media\&token=39da5228-20a5-443f-bbf9-a054c513873c)

#### 任务12：Bind读取配置到C#实例 <a href="#ren-wu-12bind-du-qu-pei-zhi-daocshi-li" id="ren-wu-12bind-du-qu-pei-zhi-daocshi-li"></a>

新建ASP.NET Core Web 应用程序OptionsBindSample，直接选择 空，确定

在Startup.cs中通过依赖注入添加configuration

```
public IConfiguration Configuration { get; set; }

public Startup(IConfiguration configuration)
{
    Configuration = configuration;
}
```

项目右键，新建项，添加一个类Class.cs

```
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace OptionsBindSample
{
    public class Class
    {
        public int ClassNo { get; set; }

        public string ClassDesc { get; set; }

        public List<Student> Students { get; set; }

    }

    public class Student
    {
        public string Name { get; set; }

        public string Age { get; set; }
    }
}
```

项目右键，新建项，添加一个Json文件appsettings.json

为什么取名appsettings.json呢？

因为Program.cs中的CreateDefaultBuilder默认读取一个名为appsettings.json的Json文件并把它的内容添加到配置文件

拷贝前面的内容到appsettings.json

```
{
  "ClassNo": "1",
  "ClassDesc": "ASP.NET Core 101",

  "Students": [
      {
        "name": "mingsonzheng",
        "age": "18"
      },
      {
        "name": "jim",
        "age": "28"
      },
      {
        "name": "tom",
        "age": "38"
      }
  ]
}
```

在Startup.cs中通过Bind读取配置

```
app.Run(async (context) =>
{
    var myClass = new Class();
    Configuration.Bind(myClass);// 实现配置文件信息与对象的映射

    await context.Response.WriteAsync($"ClassNo: { myClass.ClassNo}");
    await context.Response.WriteAsync($"ClassDesc: { myClass.ClassDesc}");
    await context.Response.WriteAsync($" {myClass.Students.Count } Students");
});
```

完整Startup.cs

```
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

namespace OptionsBindSample
{
    public class Startup
    {

        public IConfiguration Configuration { get; set; }

        // 通过依赖注入添加configuration
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        // This method gets called by the runtime. Use this method to add services to the container.
        // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
        public void ConfigureServices(IServiceCollection services)
        {
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.Run(async (context) =>
            {
                var myClass = new Class();
                Configuration.Bind(myClass);// Bind读取配置

                await context.Response.WriteAsync($"ClassNo: { myClass.ClassNo}");
                await context.Response.WriteAsync($"ClassDesc: { myClass.ClassDesc}");
                await context.Response.WriteAsync($" {myClass.Students.Count } Students");
            });
        }
    }
}
```

启动项目

![](https://256343630-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Flyk5lHXG8Cnqf7q1KuaO%2Fuploads%2FhoYtdiZM5DafdPIJ8uq4%2F031.jpg?alt=media\&token=ba7dbca0-fa7a-46ff-b834-0570a254f59c)

#### 任务13：在Core Mvc中使用Options <a href="#ren-wu-13-zai-coremvc-zhong-shi-yong-options" id="ren-wu-13-zai-coremvc-zhong-shi-yong-options"></a>

在项目OptionsBindSample新建三个文件夹目录如下

![](https://256343630-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Flyk5lHXG8Cnqf7q1KuaO%2Fuploads%2FuB8nQjOVItNFm3d1mDI3%2F032.jpg?alt=media\&token=30b9fd85-7d4e-4f33-8677-107e7b920e9a)

在Controllers文件夹右键，添加一个控制器，默认，HomeController

在Home文件夹右键，添加一个视图，默认，Index

在Startup.cs中注释掉这一段代码，不然会把整个管道提交，只输出这一段

```
//app.Run(async (context) =>
//{
//    var myClass = new Class();
//    Configuration.Bind(myClass);// Bind读取配置

//    await context.Response.WriteAsync($"ClassNo: { myClass.ClassNo}");
//    await context.Response.WriteAsync($"ClassDesc: { myClass.ClassDesc}");
//    await context.Response.WriteAsync($" {myClass.Students.Count } Students");
//});
```

依赖注入配置添加MVC

```
services.AddMvc();
```

使用默认路由

```
app.UseMvcWithDefaultRoute();
```

HomeController中通过IOptions方式依赖注入

```
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;

namespace OptionsBindSample.Controllers
{
    public class HomeController : Controller
    {
        private readonly Class _myClass;

        // 通过IOptions方式依赖注入
        public HomeController(IOptions<Class> classAccesser)
        {
            _myClass = classAccesser.Value;
        }

        public IActionResult Index()
        {
            return View(_myClass);
        }
    }
}
```

在Index中定义模型，输出

```
@model OptionsBindSample.Class
@{
    ViewData["Title"] = "Index";
}

<h2>Index</h2>
<h4>Class No: @Model.ClassNo</h4>
<h4>Class Desc: @Model.ClassDesc</h4>
<h3>
    Students:
</h3>
<div>
    @foreach (var student in Model.Students)
    {
        <span>Name: @student.Name</span>
        <span>Age: @student.Age</span>
    }
</div>
```

注册Class,可以通过Configuration读取到option

```
services.Configure<Class>(Configuration);
```

Startup.cs完整代码

```
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

namespace OptionsBindSample
{
    public class Startup
    {

        public IConfiguration Configuration { get; set; }

        // 通过依赖注入添加configuration
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        // This method gets called by the runtime. Use this method to add services to the container.
        // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
        public void ConfigureServices(IServiceCollection services)
        {
            // 注册Class,可以通过Configuration读取到option
            services.Configure<Class>(Configuration);
            // 依赖注入配置添加MVC
            services.AddMvc();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            // 使用默认路由
            app.UseMvcWithDefaultRoute();

            //app.Run(async (context) =>
            //{
            //    var myClass = new Class();
            //    Configuration.Bind(myClass);// Bind读取配置

            //    await context.Response.WriteAsync($"ClassNo: { myClass.ClassNo}");
            //    await context.Response.WriteAsync($"ClassDesc: { myClass.ClassDesc}");
            //    await context.Response.WriteAsync($" {myClass.Students.Count } Students");
            //});
        }
    }
}
```

启动项目

![](https://256343630-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Flyk5lHXG8Cnqf7q1KuaO%2Fuploads%2FhVfu5ACpshPERGA5DaFt%2F033.jpg?alt=media\&token=4e21227c-53dd-4e5d-99e9-a3f49c2998c1)

如果仅仅在视图中使用options的话，HomeController的代码有点多余，可以直接在视图中注入

Index

```
@using Microsoft.Extensions.Options;
@inject IOptions<OptionsBindSample.Class> ClassAccesser
@{
    ViewData["Title"] = "Index";
}

<h2>Index</h2>
<h4>Class No: @ClassAccesser.Value.ClassNo</h4>
<h4>Class Desc: @ClassAccesser.Value.ClassDesc</h4>
<h3>
    Students:
</h3>
<div>
    @foreach (var student in ClassAccesser.Value.Students)
    {
        <span>Name: @student.Name</span>
        <span>Age: @student.Age</span>
    }
</div>
```

HomeController

```
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;

namespace OptionsBindSample.Controllers
{
    public class HomeController : Controller
    {
        public IActionResult Index()
        {
            return View();
        }
    }
}
```

启动项目得到同样结果

#### 任务14：配置的热更新 <a href="#ren-wu-14-pei-zhi-de-re-geng-xin" id="ren-wu-14-pei-zhi-de-re-geng-xin"></a>

ASP.NET修改web.config后站点会自动重启实现热更新

ASP.NET Core不同，实现如下：

将Index的这一行

```
@inject IOptions<OptionsBindSample.Class> ClassAccesser
```

修改为

```
@inject IOptionsSnapshot<OptionsBindSample.Class> ClassAccesser
```

启动项目

![](https://256343630-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Flyk5lHXG8Cnqf7q1KuaO%2Fuploads%2F8OxoqMuTOEhOKpf4IaXV%2F034.jpg?alt=media\&token=b41e3776-adce-46a6-aaec-89c367e34e9c)

修改appsettings的ClassNo为222，保存

```
  "ClassNo": "222",
```

刷新网页

![](https://256343630-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Flyk5lHXG8Cnqf7q1KuaO%2Fuploads%2FndJWwOUuHE6isWE2XUQE%2F035.png?alt=media\&token=6e3997bd-05d6-4226-a5fa-1f950c5b94c7)

**实现原理**

对比控制台程序JsonComfigSample的Program读取配置文件

```
            // 第二个参数表示文件不存在时是否抛异常
            // 第三个参数表示配置文件更新的时候是否重新加载
            var builder = new ConfigurationBuilder()
                .AddJsonFile("class.json",false,true);
```

而在ASP.NET Core程序OptionsBindSample在Program中的CreateDefaultBuilder的源码实现了

```
        public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
            WebHost.CreateDefaultBuilder(args)
                .UseStartup<Startup>();
```

WebHost源码：\
<https://github.com/aspnet/MetaPackages/blob/master/src/Microsoft.AspNetCore/WebHost.cs>

源码里面实现热更新（165行）

```
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
      .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);
```

由于它是WebHostBuilder的一个扩展函数，所以可以覆盖该方法

```
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
    WebHost.CreateDefaultBuilder(args)
        // 如果业务场景不需要一个线程一直关注配置文件变更，可以关闭热更新
        .ConfigureAppConfiguration(config => { config.AddJsonFile("appsettings.json", false, false); })
        .UseStartup<Startup>();
```

启动项目，修改配置文件，保存，刷新网页，内容不会热更新

#### 任务15：配置框架设计浅析 <a href="#ren-wu-15-pei-zhi-kuang-jia-she-ji-qian-xi" id="ren-wu-15-pei-zhi-kuang-jia-she-ji-qian-xi"></a>

```
var builder = new ConfigurationBuilder();// 初始化Builder

builder.Add(source);// 将source添加到Builder
=>
JsonConfigurationSource source = new
JsonConfigurationSource()
{
    Path = "settings.json";
};

var configurationRoot = builder.Build();// Build
=>
foreach(var source in sources)
{
    var provider = source.Build();
    providers.add(provider);
}
return new ConfigurationRoot(providers);

configurationRoot["Key"]// Use
=>
foreach(var provider in providers.Reverse())
{
    string value;
    provider.TryGet(Key, out Value)
    return value;
}
```

![](https://256343630-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Flyk5lHXG8Cnqf7q1KuaO%2Fuploads%2FeYRxNtAA7SdYo1udiJjq%2F036.jpg?alt=media\&token=ca46afa9-9c54-4b09-ad6a-abf417ee5daa)

![](https://256343630-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Flyk5lHXG8Cnqf7q1KuaO%2Fuploads%2Fvt36ICBSJdgYSqdpbluf%2F037.jpg?alt=media\&token=cba0a122-b6cd-4cbb-8560-78504674f5e4)
