自訂 ConfigurationProvider - 實作 EnvFileConfigurationProvider

使用 .env (環境變數) 配置系統,是我們常用的手段之一,他的格式非常的簡單,就 key = value,攤平的結構

讀取 .env 檔也不是甚麼太大的問題,所以我打算將他整合到 IConfiguration

有關 Configuration 的使用方式可以參考以下

如何使用組態 Microsoft.Extensions.Configuration | 余小章 @ 大內殿堂 - 點部落 (dotblogs.com.tw)

如何使用應用程式秘密組態 | 余小章 @ 大內殿堂 - 點部落 (dotblogs.com.tw)

如何使用 Options Pattern for Microsoft.Extensions.Options (dotblogs.com.tw)

.NET Core / .NET Fx 應用程式如何在開發環境使用環境變數 | 余小章 @ 大內殿堂 - 點部落 (dotblogs.com.tw)

開發環境

  • Windows 10
  • Rider
  • .NET 6

實作

.env 的格式規則如下

  • 左邊是 key,右邊是 value
  • # 是註解

.env 格式如下圖

不要忘了,機密性資料不要上版控

 

處理檔案的時候,一行一行解析等號,然後用 Environment.SetEnvironmentVariable() 設定環境變數

public class EnvFileConfigurationProvider : ConfigurationProvider
{
    private readonly string _envFile;

    public EnvFileConfigurationProvider(string envFile)
    {
        this._envFile = envFile;
    }

    public override void Load()
    {
        var filePath = this._envFile;
        if (!File.Exists(filePath))
        {
            return;
        }

        foreach (var line in File.ReadAllLines(filePath))
        {
            if (line.Substring(0, 1) == "#")
            {
                continue;
            }

            var parts = line.Split('=',
                                   StringSplitOptions.RemoveEmptyEntries);

            if (parts.Length != 2)
            {
                continue;
            }

            Environment.SetEnvironmentVariable(parts[0], parts[1]);
        }
    }
}

 

實作 IConfigurationSource,由這個物件決定要用哪一個 IConfigurationProvider 

public class EnvFileConfigurationSource : IConfigurationSource
{
    private readonly string _envFile;

    public EnvFileConfigurationSource(string envFile)
    {
        this._envFile = envFile;
    }

    public IConfigurationProvider Build(IConfigurationBuilder builder)
    {
        return new EnvFileConfigurationProvider(this._envFile);
    }
}

 

在擴充方法裡面建立 EnvFileConfigurationSource 並且調用 AddEnvironmentVariables

public static class EnvFileConfigurationExtensions
{
    public static IConfigurationBuilder AddEnvFile(this IConfigurationBuilder builder, string envFile)
    {
        var source = new EnvFileConfigurationSource(envFile);
        builder.Add(source);
        builder.AddEnvironmentVariables();
        return builder;
    }
}

 

調用方式如下,這裡我使用 ConfigurationBuilder

[TestMethod]
public void 讀取ENV檔案()
{
    var configRoot = new ConfigurationBuilder()
                     .AddEnvFile("secret.env")
                     .Build()
        ;
    Assert.AreEqual("foo-bar", configRoot.GetSection("SQL_SERVER_CS").Value);
    Assert.AreEqual("localhost:6379", configRoot.GetSection("REDIS_ENDPOINT").Value); 
}

 

[TestMethod]
public void 讀取ENV檔案後綁定()
{
    var configRoot = new ConfigurationBuilder()
                     .AddEnvFile("secret.env")
                     .Build()
        ;
    var appSetting = configRoot.Get<AppSetting>();
    Assert.AreEqual("foo-bar", appSetting.SQL_SERVER_CS);
    Assert.AreEqual("localhost:6379", appSetting.REDIS_ENDPOINT);
}

 

參考資源

Using .env in .NET - Dusted Codes

範例位置

sample.dotblog/Configuration/Lab.EnvFileConfig at 23bd21a9c729910bc8f7d39ea89799a7d83cc521 · yaochangyu/sample.dotblog (github.com)

套件

以下套件還提供其他資料格式的讀取,比如像是 yaml

andrewlock/NetEscapades.Configuration: Additional configuration providers to use with ASP.NET Core (github.com)

若有謬誤,煩請告知,新手發帖請多包涵


Microsoft MVP Award 2010~2017 C# 第四季
Microsoft MVP Award 2018~2022 .NET

Image result for microsoft+mvp+logo