9

用编译配置与环境变量实现开发时切换配置文件 - 波多尔斯基

 1 year ago
source link: https://www.cnblogs.com/podolski/p/16777738.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

开发人员在开发代码的时候,经常会使用到DebugReleaseDevelopmentProduction等几个概念,虽然有些地方在功能上最终殊途同归,但是还是有非常大的区别。

首先需要搞清楚,Debug、Release都属于编译配置,而Development、Production则属于环境配置。

作为开发者,开发时如果需要切换开发与发布环境的配置文件,两种方案都可以实现。

编译配置#

思路很简单,在Debug模式下,就使用开发版本的配置项,在Release模式下,就使用正式版的配置项。开发的时候,只要通过鼠标点点切换就好了,非常方便。

616093-20221010221908436-1050267070.png

Debug与Release控制编译器的行为,两者的区别挺多的,其中编译器优化这个可以查看我的一篇上古的文章。Debug配置定义了编译符号:DEBUG,Release定义了编译符号:RELEASE。
在代码中可以通过两种方式感知这个编译符号:

使用编译器预处理指令#if#

public static async Task Main(string[] args)
{
    IHost host = Host.CreateDefaultBuilder(args)
        .ConfigureServices(services =>
        {
           var provider = services.BuildServiceProvider();
#if DEBUG
           services.AddDbContext<ManagementDataContext>(options =>
        options.UseNpgsql(provider.GetRequiredService<IConfiguration>().GetConnectionString("DebugConnection")));
#else
           services.AddDbContext<ManagementDataContext>(options =>
        options.UseNpgsql(provider.GetRequiredService<IConfiguration>().GetConnectionString("ReleaseConnection")));
#endif
        })
        .Build();

    await host.RunAsync();
}

编译器预处理语句,优点是简单,缺点是需要选择的分支特别多时,显得非常乱。

预处理语句将在编译中直接生成对应的代码,编译完成的程序中,看不到任何有关选择的过程。

使用ConditionalAttribute#

ConditionalAttribute是一种特性标识,可以读取调用方(如果没有就是自己)的编译符号自动选择代码。

[Conditional("DEBUG")]
private static void ConfigNpgsqlDebug(IServiceCollection services)
{
    var provider = services.BuildServiceProvider();
    services.AddDbContext<ManagementDataContext>(options =>
        options.UseNpgsql(provider.GetRequiredService<IConfiguration>().GetConnectionString("DebugConnection")));
}
[Conditional("RELEASE")]
private static void ConfigNpgsqlRelease(IServiceCollection services)
{
    var provider = services.BuildServiceProvider();
    services.AddDbContext<ManagementDataContext>(options =>
        options.UseNpgsql(provider.GetRequiredService<IConfiguration>().GetConnectionString("ReleaseConnection")));
}

public static async Task Main(string[] args)
{
    IHost host = Host.CreateDefaultBuilder(args)
        .ConfigureServices(services =>
        {
ConfigNpgsqlDebug(services);
ConfigNpgsqlRelease(services);
        })
        .Build();

    await host.RunAsync();
}

注意,编译器会根据实际情况将对应代码编译进程序中,Debug模式下,Release段的代码和对应的引用都不会执行。这种方式比较优雅简洁,不过只能标识方法或者属性,有一定局限性。

ConditionalAttribute会保留到最终的程序集中,因此在编译后的程序中能看到这个Attribute。

环境配置#

思路是程序在运行的过程中,读取环境变量,通过不同的环境变量切换不同的配置文件。

关于环境配置官方有一篇非常详细的文档。我们就使用默认的Development和Production两种环境变量配置。环境变量并不能在代码中固化,在开发时,需要使用设置对应的IDE环境。将配置分别写在appsettings.Development.jsonappsettings.Production.json两个文件中,配置项目都为MonitorConnection,只是值不同。

官方文档对配置文件会加载appsettings.{Environment}.json,默认情况下,如果不指定环境变量,那么会认为是Production

调用代码非常简单,不需要对环境进行的特别识别:

public static async Task Main(string[] args)
{
    IHost host = Host.CreateDefaultBuilder(args)
        .ConfigureServices(services =>
        {
            services.AddDbContext<ManagementDataContext>(options =>
                options.UseNpgsql(Configuration.GetConnectionString("MonitorConnection")));
        })
        .Build();

    await host.RunAsync();
}

然后我们设置IDE,以VS为例,找到项目对应的launchSettings.json,修改成类似如下的代码。

"profiles": {
    "WebApi(Dev)": {
      "commandName": "Project",
      "launchBrowser": true,
      "launchUrl": "swagger",
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      }
    },
    "WebApi(Prod)": {
      "commandName": "Project",
      "launchBrowser": true,
      "launchUrl": "swagger",
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Production"
      }
    }
  }

保存文件,在IDE上面就能出现这样的界面。
616093-20221010223728802-1492143049.png

这样就能通过选择不同的运行环境来自动切换不同的配置文件了。

总结#

两种方法虽然最终的结果类似,但是区别还是挺大的:

  • 使用编译配置,运行时将保持固定,不能再切换;而环境配置可以通过变更环境变量的形式动态调整(对docker友好)。
  • 使用编译配置,往往同时绑定编译时候的其他flag(优化、pdb生成之类),不是非常灵活。
  • 代码上,使用编译配置往往需要使用更多的代码实现,而环境变量往往不需要额外的代码。

综上,个人认为使用环境变量进行切换会更加方便与灵活。

参考#


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK