用编译配置与环境变量实现开发时切换配置文件

打印 上一主题 下一主题

主题 924|帖子 924|积分 2772

开发人员在开发代码的时候,经常会使用到Debug、Release、Development、Production等几个概念,虽然有些地方在功能上最终殊途同归,但是还是有非常大的区别。
首先需要搞清楚,Debug、Release都属于编译配置,而Development、Production则属于环境配置。
作为开发者,开发时如果需要切换开发与发布环境的配置文件,两种方案都可以实现。
编译配置

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

Debug与Release控制编译器的行为,两者的区别挺多的,其中编译器优化这个可以查看我的一篇上古的文章。Debug配置定义了编译符号:DEBUG,Release定义了编译符号:RELEASE。
在代码中可以通过两种方式感知这个编译符号:
使用编译器预处理指令#if
  1. public static async Task Main(string[] args)
  2. {
  3.     IHost host = Host.CreateDefaultBuilder(args)
  4.         .ConfigureServices(services =>
  5.         {
  6.            var provider = services.BuildServiceProvider();
  7. #if DEBUG
  8.            services.AddDbContext<ManagementDataContext>(options =>
  9.         options.UseNpgsql(provider.GetRequiredService<IConfiguration>().GetConnectionString("DebugConnection")));
  10. #else
  11.            services.AddDbContext<ManagementDataContext>(options =>
  12.         options.UseNpgsql(provider.GetRequiredService<IConfiguration>().GetConnectionString("ReleaseConnection")));
  13. #endif
  14.         })
  15.         .Build();
  16.     await host.RunAsync();
  17. }
复制代码
编译器预处理语句,优点是简单,缺点是需要选择的分支特别多时,显得非常乱。
预处理语句将在编译中直接生成对应的代码,编译完成的程序中,看不到任何有关选择的过程。
使用ConditionalAttribute

ConditionalAttribute是一种特性标识,可以读取调用方(如果没有就是自己)的编译符号自动选择代码。
  1. [Conditional("DEBUG")]
  2. private static void ConfigNpgsqlDebug(IServiceCollection services)
  3. {
  4.     var provider = services.BuildServiceProvider();
  5.     services.AddDbContext<ManagementDataContext>(options =>
  6.         options.UseNpgsql(provider.GetRequiredService<IConfiguration>().GetConnectionString("DebugConnection")));
  7. }
  8. [Conditional("RELEASE")]
  9. private static void ConfigNpgsqlRelease(IServiceCollection services)
  10. {
  11.     var provider = services.BuildServiceProvider();
  12.     services.AddDbContext<ManagementDataContext>(options =>
  13.         options.UseNpgsql(provider.GetRequiredService<IConfiguration>().GetConnectionString("ReleaseConnection")));
  14. }
  15. public static async Task Main(string[] args)
  16. {
  17.     IHost host = Host.CreateDefaultBuilder(args)
  18.         .ConfigureServices(services =>
  19.         {
  20. ConfigNpgsqlDebug(services);
  21. ConfigNpgsqlRelease(services);
  22.         })
  23.         .Build();
  24.     await host.RunAsync();
  25. }
复制代码
注意,编译器会根据实际情况将对应代码编译进程序中,Debug模式下,Release段的代码和对应的引用都不会执行。这种方式比较优雅简洁,不过只能标识方法或者属性,有一定局限性。
ConditionalAttribute会保留到最终的程序集中,因此在编译后的程序中能看到这个Attribute。
环境配置

思路是程序在运行的过程中,读取环境变量,通过不同的环境变量切换不同的配置文件。
关于环境配置官方有一篇非常详细的文档。我们就使用默认的Development和Production两种环境变量配置。环境变量并不能在代码中固化,在开发时,需要使用设置对应的IDE环境。将配置分别写在appsettings.Development.json和appsettings.Production.json两个文件中,配置项目都为MonitorConnection,只是值不同。
官方文档对配置文件会加载appsettings.{Environment}.json,默认情况下,如果不指定环境变量,那么会认为是Production。
调用代码非常简单,不需要对环境进行的特别识别:
  1. public static async Task Main(string[] args)
  2. {
  3.     IHost host = Host.CreateDefaultBuilder(args)
  4.         .ConfigureServices(services =>
  5.         {
  6.             services.AddDbContext<ManagementDataContext>(options =>
  7.                 options.UseNpgsql(Configuration.GetConnectionString("MonitorConnection")));
  8.         })
  9.         .Build();
  10.     await host.RunAsync();
  11. }
复制代码
然后我们设置IDE,以VS为例,找到项目对应的launchSettings.json,修改成类似如下的代码。
  1. "profiles": {
  2.     "WebApi(Dev)": {
  3.       "commandName": "Project",
  4.       "launchBrowser": true,
  5.       "launchUrl": "swagger",
  6.       "environmentVariables": {
  7.         "ASPNETCORE_ENVIRONMENT": "Development"
  8.       }
  9.     },
  10.     "WebApi(Prod)": {
  11.       "commandName": "Project",
  12.       "launchBrowser": true,
  13.       "launchUrl": "swagger",
  14.       "environmentVariables": {
  15.         "ASPNETCORE_ENVIRONMENT": "Production"
  16.       }
  17.     }
  18.   }
复制代码
保存文件,在IDE上面就能出现这样的界面。

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

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

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


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
回复

使用道具 举报

0 个回复

正序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

道家人

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表