ASP .NET CORE 根据环境变量支持多个 appsettings.json

0.背景

在开发项目的过程中,生产环境与调试环境的配置确定是不同的。拿个最简单的例子来讲,好比链接字符串这种东西,调试环境确定是不能链接生产数据库的。在以前的话,这种状况只能说是你 COPY 两个同名的配置文件来进行处理。而后你在本地就使用本地的配置,生产环境就使用生产环境的配置文件,十分麻烦。linux

而 ASP .NET CORE 支持利用环境变量来动态配置 JSON 文件,下面就来看一下吧。docker

1.准备工做

首先在你的 ASP .NET CORE 项目当中添加一个 appsettings.json 文件,内容以下:shell

{
  "ConnectionString": {
    "Default": "Normal Database"
  }
}

以后再继续添加一个 appsettings.Development.json,以后在你的解决方案管理器就会看到下面这种状况。数据库

更改其内容以下:json

{
  "ConnectionString": {
    "Default": "Development Database"
  }
}

以后呢,咱们继续添加一个生产环境的配置文件,名字叫作 appsettings.Production.json ,更改其内容以下:windows

{
  "ConnectionString": {
    "Default": "Production Database"
  }
}

最后咱们的文件应该以下图:缓存

以上就是咱们的准备工做,咱们准备了两个环境的配置文件以及一个默认状况的配置文件,下面我就就来看看如何应用环境变量来达到咱们想要的效果。app

2.环境控制

在项目调试的时候,咱们能够经过右键项目属性,跳转到调试能够看到一个环境变量的设定,经过更改 ASPNETCORE_ENVIRONMENT 的值来切换不一样环境。ide

能够看到目前咱们处于 Development 也就是开发环境,那么按照咱们的设想,就应该读取 appsettings.Development.json 的文件数据了。测试

2.编写代码

新建一个 AppConfigure 静态类,他的内部有一个字典,用于缓存不一样环境不一样路径的 IConfigurationRoot 配置。

public static class AppConfigure
{
    // 缓存字典
    private static readonly ConcurrentDictionary<string, IConfigurationRoot> _cacheDict;

    static AppConfigure()
    {
        _cacheDict = new ConcurrentDictionary<string, IConfigurationRoot>();
    }

    // 传入 JSON 文件夹路径与当前的环境变量值
    public static IConfigurationRoot GetConfigurationRoot(string jsonDir, string environmentName = null)
    {
        // 设置缓存的 KEY
        var cacheKey = $"{jsonDir}#{environmentName}";

        // 添加默认的 JSON 配置
        var builder = new ConfigurationBuilder().SetBasePath(jsonDir).AddJsonFile("appsettings.json", optional: true, reloadOnChange: true);

        // 根据环境变量添加相应的 JSON 配置文件
        if (!string.IsNullOrEmpty(environmentName))
        {
            builder = builder.AddJsonFile($"appsettings.{environmentName}.json", optional: true, reloadOnChange: true);
        }

        // 返回构建成功的 IConfigurationRoot 对象
        return builder.Build();
    }
}

用法的话也很简单:

public Startup(IHostingEnvironment env)
{
    var configurationRoot = AppConfigure.GetConfigurationRoot(env.ContentRootPath, env.EnvironmentName);
    Console.WriteLine(configurationRoot["ConnectionString:Default"]);
}

3.测试

测试的话直接更改环境变量就能够看到效果了,更改其值为 Production。

如今咱们来运行,而且添加一个监视变量。

看样子它如今读取的就是咱们的生产环境的数据了。

4.代码分析

其实吧,也不用这么麻烦,在 Startup.cs 经过构造注入获得的 IConfiguration 就是按照 GetConfigurationRoot() 这个方法来进行构建的,你直接使用 Configuration/ConfigurationRoot 的索引器就能够访问到与环境变量相应的 JSON 文件了。

可能你还不太理解,明明在 GetConfigurationRoot() 方法里面使用 AddJsonFile() 方法只是添加了两次个 Provider ,为何在使用索引器访问 JSON 配置的时候就是使用的当前环境的 JSON 文件呢?

我其实觉得最开始 .NET CORE 对于 IConfiguration 的索引器实现就是读取了当前环境变量,而后根据这个环境变量去匹配对应的 Provider 取得值。

最后翻阅了 .NET CORE 的源代码以后发现是我想错了,其实他就是单纯的翻转了一下 Providers 的集合,而后取的第一个元素。

// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using Microsoft.Extensions.Primitives;

namespace Microsoft.Extensions.Configuration
{
    public class ConfigurationRoot : IConfigurationRoot
    {
        private IList<IConfigurationProvider> _providers;
        private ConfigurationReloadToken _changeToken = new ConfigurationReloadToken();

        // 初始化 ConfigurationRoot 的时候传入配置提供者
        public ConfigurationRoot(IList<IConfigurationProvider> providers)
        {
            if (providers == null)
            {
                throw new ArgumentNullException(nameof(providers));
            }

            _providers = providers;
            foreach (var p in providers)
            {
                p.Load();
                ChangeToken.OnChange(() => p.GetReloadToken(), () => RaiseChanged());
            }
        }

        public IEnumerable<IConfigurationProvider> Providers => _providers;


        public string this[string key]
        {
            get
            {
                // 反转 Providers ,以后遍历
                foreach (var provider in _providers.Reverse())
                {
                    string value;

                   // 若是拿到了值,直接返回,再也不遍历
                    if (provider.TryGet(key, out value))
                    {
                        return value;
                    }
                }

                return null;
            }

            set
            {
                if (!_providers.Any())
                {
                    throw new InvalidOperationException(Resources.Error_NoSources);
                }

                foreach (var provider in _providers)
                {
                    provider.Set(key, value);
                }
            }
        }
    }

    // ... 省略了的代码
}

回到第三节所写的代码,能够看到咱们首先添加的是 appsettings.json 而后再根据环境变量添加的 $"appsettings.{environmentName}.json" ,因此反转以后取得的确定就是带环境变量的配置文件咯。

5.不一样 OS 的环境变量配置

5.1 Windows

直接右键计算机手动添加环境变量。

5.2 Linux

使用 export 命令直接进行环境变量设置。

export ASPNETCORE_ENVIRONMEN='Production'

5.3 Docker

Docker 配置最为简单,直接在启动容器的时候加上 -e 参数便可,例如:

docker run -d -e ASPNETCORE_ENVIRONMENT=Production --name testContainer testImage
相关文章
相关标签/搜索