.NET 6 應用程式如何切換環境和組態

以往我們在 .Net Fx 可以透過 Transforms 來切換不同的組態,而在 .NET Core 開始,則改用 Microsoft.Extensions.Configuration 來處理組態,上篇 也針對 Microsoft.Extensions.Configuration 進行了介紹,這篇將針對 .NET 應用程式注入環境的做法做一個簡單的介紹

開發環境

  • .NET 6
  • Rider 2022.2.2

環境變數

.NET Core 會從下列環境變數讀取

  • DOTNET_ENVIRONMENT
  • ASPNETCORE_ENVIRONMENT,預設 ASP.NET Core 會呼叫 WebApplication.CreateBuilder 建立應用程式,這時會讀取 ASPNETCORE_ENVIRONMENT,ASPNETCORE_ENVIRONMENT會覆寫 DOTNET_ENVIRONMENT的值

IHostEnvironment.EnvironmentName 有以下幾種狀態

  • Development:開發環境,預設,在本地開發環境的 launchSettings.json 檔案設定 ASPNETCORE_ENVIRONMENT = Development。
  • Staging:暫存環境(測試環境)。
  • Production:正式環境,預設,當沒有設定 DOTNET_ENVIRONMENT 和 ASPNETCORE_ENVIRONMENT 時,使用 Production。

launchSettings.json 是給本地 IDE 使用通常不會上版控,就算要上版控也不要存放機密性資料

機密性資料的做法可以參考

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

在開發中安全儲存應用程式密碼,ASP.NET Core | Microsoft Docs

IHostEnvironment.Environment 也有提供判斷目前應用程式是不是某一種環境,除了官方預設提供的三種環境之外,也可以自行擴充環境,這時候可以使用 IsEnvironment 判斷

 

根據環境切換組態設定

首先要先有 appsettings.json,然後再產生 appsettings.Development.json、appsettings.Staging.json、appsettings.Production.json

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "Extension": {
    "Version": "Default"
  }
}

 

然後再產生

appsettings.Development.json

{
  "Extension": {
    "Version": "Development"
  }
}

 

appsettings.Staging.json

{
  "Extension": {
    "Version": "Staging"
  }
}

 

appsettings.Production.json

{
  "Extension": {
    "Version": "Production"
  }
}

 

讀取組態設定檔時,先讀取 appsettings.json,指定該檔為必要檔案,再讀取 appsettings.{env.EnvironmentName}.json 檔案。當 Key 值重複時,後面載入的設定會蓋掉前面的組態。

var environmentName = builder.Environment.EnvironmentName;
var configRoot = new ConfigurationBuilder()
    .SetBasePath(Directory.GetCurrentDirectory())
    .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
    .AddJsonFile($"appsettings.{environmentName}.json", optional: true, reloadOnChange: true)
    .Build();
    ;

builder.Configuration.AddConfiguration(configRoot);

 

注入環境變數

了解了如何根據環境切換組態設定後,我們再來看看要怎麼樣注入環境

從 dotnet CLI

通過 --environment 注入

dotnet run --environment Staging

 

執行結果如下

 

從 IDE 的 launchSettings.json

不同的 IDE 所需要的 launch.json 都不太一樣,VS IDE / Rider 都有 IDE 可以產生 launchSettings.json

 

ASPNETCORE_ENVIRONMENT 節點在這個檔案

 

套用 launch profile

dotnet run --launch-profile "SampleApp"

 

設定 Process 的環境變數 (Windows)

生命週期僅在一個 Process,先透過指令碼設定環境變數,再執行 dotnet run

Cmd

set ASPNETCORE_ENVIRONMENT=Staging
dotnet run --no-launch-profile

 

PowerShell

$Env:ASPNETCORE_ENVIRONMENT = "Staging"
dotnet run --no-launch-profile

 

Rider

除了 launchSettings.json 能注入環境變數,Rider 也有提供 UI 注入環境,要稍微注意的是 Template 不太一樣

或者也是可以先用指令碼注入環境變數,再用只指令碼啟動 Rider

$Env:ASPNETCORE_ENVIRONMENT = "Staging"
cd "C:\Users\Yao Chang Yu\scoop\apps\Rider-EAP\current\IDE\bin\"
./rider64.exe

 

設定 Process 的環境變數 (macOS)

Bash

ASPNETCORE_ENVIRONMENT=Staging 
dotnet run

或者是在應用程式執行之前使用export

export ASPNETCORE_ENVIRONMENT=Staging

web.config (Windows)

設定 web.config 檔案,在 aspNetCore 節點設定 <environmentVariables>

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <location path="." inheritInChildApplications="false">
    <system.webServer>
      <handlers>
        <add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModuleV2" resourceType="Unspecified" />
      </handlers>
      <aspNetCore processPath="dotnet" arguments=".\Lab.Host.Env.WebApi.dll" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" hostingModel="inprocess">
        <environmentVariables>
          <environmentVariable name="ASPNETCORE_ENVIRONMENT" value="Staging" />
        </environmentVariables>
      </aspNetCore>
    </system.webServer>
  </location>
</configuration>

 

IIS 部署 (Windows)

  • 在發行設定檔(publish profile)指定 EnvironmentName 屬性,部署時就會在 web.config 設定環境變數

下面的例子是透過指令碼打包 web site 時設定 EnvironmentName 屬性。

msbuild
"C:\Program Files\Microsoft Visual Studio\2022\Enterprise\MSBuild\Current\Bin\MSBuild.exe" ^
Lab.Host.Env.WebApi.csproj ^
/p:DeployOnBuild=true ^
/p:Configuration=release ^
/p:EnvironmentName=Staging ^
/p:PrecompileBeforePublish=false

 

msbuild 執行結果如下:

 

dotnet build
dotnet build ^
Lab.Host.Env.WebApi.csproj ^
/p:DeployOnBuild=true ^
/p:Configuration=release ^
/p:EnvironmentName=Staging ^
/p:PrecompileBeforePublish=false

 

dotnet build 執行結果如下:

 

dotnet publish
dotnet publish ^
Lab.Host.Env.WebApi.csproj ^
/p:Configuration=release ^
/p:EnvironmentName=Staging ^
/p:PrecompileBeforePublish=false

 

dotnet publish 執行結果如下:

 

msbuild、dotnet build、dotnet publish 都可以產生出 web.config 包含了 EnvironmentName 屬性 

 

web.config 內容如下:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <location path="." inheritInChildApplications="false">
    <system.webServer>
      <handlers>
        <add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModuleV2" resourceType="Unspecified" />
      </handlers>
      <aspNetCore processPath="dotnet" arguments=".\Lab.Host.Env.WebApi.dll" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" hostingModel="inprocess">
        <environmentVariables>
          <environmentVariable name="ASPNETCORE_ENVIRONMENT" value="Staging" />
        </environmentVariables>
      </aspNetCore>
    </system.webServer>
  </location>
</configuration>

 

這裡有有過往的打包指令

"C:\Program Files\Microsoft Visual Studio\2022\Enterprise\MSBuild\Current\Bin\MSBuild.exe" ^
Lab.Host.Env.WebApi.csproj ^
/p:DeployOnBuild=true ^
/p:WebPublishMethod=Package ^
/p:PackageAsSingleFile=true ^
/p:PackageLocation="bin\WebPackage" ^
/p:Configuration=release ^
/p:EnvironmentName=Staging ^
/p:PrecompileBeforePublish=false

打包的效果如下

最後,通過打包好的 *deploy.cmd + WebDeploy Service 部署到 IIS 

Lab.Host.Env.WebApi.deploy.cmd /y /m:https://$(WebDeploy.Server):8172/MSDeploy.axd /i:true /u:$(WebDeploy.UserId) /p:$(WebDeploy.Password) /a:Basic "-AllowUntrusted=True" "-setParam:name='IIS Web Application Name',value='$(WebDeploy.IISApp)'"

 

taskfile

當指令碼越來越多時,我們會用 task 集中管理,把剛剛提到的 dotnet run 放進去

# Taskfile.yml
version: "3"
dotenv: [ "secrets/secrets.env" ]
tasks:
 webapi:
   desc: WebApi Development
   dir: "src/Lab.Host.Env.WebApi"
   cmds:
     - dotnet run --environment Staging

 

執行結果如下

 

作業系統的環境變數(Windows)

以下操作需要管理員的權限

Cmd

setx ASPNETCORE_ENVIRONMENT Staging /M

 

PowerShell

[Environment]::SetEnvironmentVariable("ASPNETCORE_ENVIRONMENT", "Staging", "Machine")

 

在 Windows 環境變數

'-larilble 
Variable 
Cencel

 

 

 

從程式碼注入

通過 WebApplicationOptions 指定

WebApplication.CreateBuilder
var builder = WebApplication.CreateBuilder(new WebApplicationOptions
{
    EnvironmentName = Environments.Staging
}); 

// Add services to the container.
builder.Services.AddRazorPages();

var app = builder.Build();

builder.Environment

除了在 WebApplication.CreateBuilder 注入,也可以後續透過 builder.Environment 注入,Environment 並不是唯讀物件所以可以在任意階段注入你想要的,不過,建議集中建立應用程式的地方決定就好,不要有太多地方可以改變這個狀態

通過 System.Envrionment 設定

NET 提供 System.Environment.GetEnvironmentVariableSystem.Environment.SetEnvironmentVariable 靜態方法讓我們操作環境變數

 

參考

在 ASP.NET Core 中使用多個環境 | Microsoft Docs

Visual Studio 發佈設定檔 (.pubxml) 以進行 ASP.NET Core應用程式部署 | Microsoft Docs

web.config 檔案 | Microsoft Docs

延伸閱讀

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

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

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

範例位置

sample.dotblog/Host/Lab.Host.Env at master · yaochangyu/sample.dotblog (github.com)

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


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

Image result for microsoft+mvp+logo