如何在.Net Core应用程序中发布特定于环境的appsettings?

98

我在我的 .Net Core 应用程序中有 3 个针对不同环境的 appsettings 文件。

enter image description here

project.json 中,我设置了 publishOptions 如下所示(根据此处的建议here)。

"publishOptions": {
    "include": [
      "wwwroot",      
      "appsettings.development.json",
      "appsettings.staging.json",
      "appsettings.production.json",
      "web.config"
    ]
  },

我有三个相应的启动类,根据环境使用适当的appsettings

var builder = new ConfigurationBuilder()
    .SetBasePath(env.ContentRootPath)
    .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: false, reloadOnChange: true);

然而,当我发布应用程序时,所有三个appsettings文件都会出现在所有环境中。如何发布特定于环境的appsetting文件?


27
请问有人能回答这个问题吗?把所有的 appsettings.json 文件发布到所有环境中真的没有意义。 - LP13
只要您在Windows上托管,就没有问题,但如果您切换到区分大小写的操作系统(*nix),那么就会出现问题。例如,微软的EnvironmentName字符串采用PascalCase格式。 - Granger
3
你找到解决方案了吗?我正在使用asp.netcore 3.1。 - fiberOptics
1
并不是真的。目前我的PowerShell脚本会复制整个发布文件夹,然后删除不需要的appsettings。请注意,即使您不删除appsettings,您的应用程序也不会使用所有。您的应用程序将仅使用appsettings.jsonappsettings.{environmentname}.json。在启动时加载文件的顺序在这里非常重要。 - LP13
3
我对.NET Core中缺少的所有基本功能感到非常厌烦,这使得它难以被认真对待。 - Scott Wilson
17个回答

34

如果有人想知道如何在多个环境中使用不同的appsettings,这里提供了一种可能的解决方案。

dotnet publish --configuration [Debug|Release]会将适当的appsettings.json文件复制到发布文件夹中,如果*.csproj对这些文件有条件逻辑:

  • 首先,在.pubxml发布配置文件中(可以在Visual Studio的属性->发布配置文件中找到),禁用默认情况下包括所有内容文件
<PropertyGroup>
    <TargetFramework>netcoreapp2.1</TargetFramework>
    <EnableDefaultContentItems>false</EnableDefaultContentItems>
</PropertyGroup>
  • 然后指定条件调试/发布逻辑
<Choose>
    <When Condition="'$(Configuration)' == 'Debug'">
      <ItemGroup>
        <None Include="appsettings.json" CopyToOutputDirectory="Always" CopyToPublishDirectory="Always" />
        <None Include="appsettings.prod.json" CopyToOutputDirectory="Never" CopyToPublishDirectory="Never" />
      </ItemGroup>
    </When>
    <When Condition="'$(Configuration)' == 'Release'">
      <ItemGroup>
        <None Include="appsettings.json" CopyToOutputDirectory="Never" CopyToPublishDirectory="Never" />
        <None Include="appsettings.prod.json" CopyToOutputDirectory="Always" CopyToPublishDirectory="Always" />
      </ItemGroup>
    </When>
</Choose>
  • 最后,在Startup.cs中尝试加载两个文件
public Startup(IHostingEnvironment env)
{
    var builder = new ConfigurationBuilder()
        .SetBasePath(env.ContentRootPath)
        .AddJsonFile($"appsettings.prod.json", optional: true, reloadOnChange: true)
        .AddJsonFile($"appsettings.json", optional: true, reloadOnChange: true)
        .AddEnvironmentVariables();

    Configuration = builder.Build();
}

我希望这个解决方案对你有所帮助。


7
未按照所提问的问题进行回答。 - Chris G. Williams
6
我认为它回答了提出的问题。每个环境在发布时只会获取到相关的appsettings文件。 - ztorstri
2
@Ben,这个答案实际上必须被标记为答案!它对我非常有效! - RisingHerc
2
“好知道”的方法,但并没有回答问题,因为它明确指出了针对不同环境(如staging、production、dev等)进行发布,但这个答案试图使用“配置”来解决问题。 - yekanchi
这个答案只有一个小问题:你的 appsettings.xyz.json 必须包含原始文件的所有属性,但对于配置模式来说并非如此。幸运的是,解决方案非常简单!首先,将 appsettings.json 注册为第一个文件,然后注册所有其他环境特定的文件。之后,你可以修改你的 .csproj 逻辑,始终包括 appsetting.json,而只包括一个其他基于配置/环境名称的特定环境文件。 - Gianni B.

31

我最近也需要找到一个解决方案,我通过向.csproj文件添加一些设置和对Program.cs进行微小更改来实现它。

<Project Sdk="Microsoft.NET.Sdk.Web">
    <!-- ... -->
    <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|AnyCPU'">
        <DebugSymbols>true</DebugSymbols>
        <DebugType>full</DebugType>
        <DefineConstants>DEBUG;TRACE</DefineConstants>
        <EnvironmentName>Development</EnvironmentName>
    </PropertyGroup>
    <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|AnyCPU'">
        <DebugType>pdbonly</DebugType>
        <Optimize>true</Optimize>
        <EnvironmentName>Production</EnvironmentName>
    </PropertyGroup>
    <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Stage|AnyCPU'">
        <DebugType>pdbonly</DebugType>
        <Optimize>true</Optimize>
        <EnvironmentName>Staging</EnvironmentName>
    </PropertyGroup>
    <ItemGroup>
        <Content Remove="appsettings.json" />
        <Content Remove="appsettings.*.json" />
    </ItemGroup>
    <ItemGroup>
        <Content Include="appsettings.json" CopyToOutputDirectory="PreserveNewest" />
        <Content Include="appsettings.*.json" Exclude="appsettings.$(EnvironmentName).json" DependentUpon="appsettings.json" CopyToOutputDirectory="Never" />
        <Content Include="appsettings.$(EnvironmentName).json" DependentUpon="appsettings.json" CopyToOutputDirectory="PreserveNewest" />
    </ItemGroup>

    <Target Name="RenameAppsettings" AfterTargets="Publish">
        <Move SourceFiles="$(PublishDir)\appsettings.$(EnvironmentName).json" DestinationFiles="$(PublishDir)\appsettings.overrides.json" />
    </Target>
</Project>
为了更好的解释,我为每个配置添加了一个<EnvironmentName>元素,这样可以在构建过程中使用。我使用appsettings.{EnvironmentName}.json(如appsettings.Staging.json)作为“重写”文件,所以我只需要在构建过程中将其重命名即可。例如,当您运行dotnet publish -c Stage时,它会将appsettings.Staging.json文件发布到发布文件夹并将其重命名为appsettings.overrides.json。在您的Program.cs中,您只需要包含appsettings.overrides.json文件即可:
    .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
    .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true)
    .AddJsonFile($"appsettings.overrides.json", optional: true, reloadOnChange: true)

希望对您有所帮助!

顺便提一下:我包含 appsettings.*.json 并将其设置为 CopyToOutputDirectory="Never",这样在开发时它们仍会出现在 Visual Studio 中。否则,如果您只想要当前环境的 appsettings 文件在 VS 中显示,只需从 csproj 文件中删除该行。


啊,我明白了。获取发布目录的值是 $(PublishDir)。你应该能够用 $(PublishDir) 替换我给出的解决方案中的 $(OutputPath)\publish,这样就可以实现你想要的效果了。我也已经更新了答案,使用 $(PublishDir) 代替了原来的方式,因为这样更好,可以支持 --output。感谢你提出这个问题! - Sensei_Shoh
我应该猜到的!当我没有看到完整的符号列表时,我只是临时硬编码它了。非常感谢您! - DeannaD
1
@Sensei_Shoh,我尝试了你的解决方案,在本地一切都运行良好。但是,当部署到服务器时,出现错误“找不到配置文件'appsettings.Development.json',且不可选”,即使我在发布中指定了“Testing”环境,也是如此? - Tim
@Tim 你的 Program.cs 文件中是否包含了在 appsettings.{env.EnvironmentName}.json 中设置为 optional:true 的代码?例如:`.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true)`你应该根据 <Target Name="RenameAppsettings" AfterTargets="Publish">... 配置,将你的 {env.EnvironmentName}.json (在你这个情况下是 appsettings.Development.json)重命名为 appsettings.overrides.json 。当你部署时,你的 Program.cs 文件只会读取/查找 appsettings.overrides.json - Sensei_Shoh
我喜欢这个答案,但遇到了一个问题。VS 2019 .met core 3.1。在“发布”期间,它会寻找appsettings..json并报错。我不确定该怎么办。 - 7 Reeds
显示剩余3条评论

13
简单的解决方法是在发布配置文件(.pubxml)中指定环境名称。这样您就不需要将其添加到环境变量中,可以在同一服务器上托管具有多个配置的应用程序。
<EnvironmentName>{Enviornment name}</EnvironmentName>

在发布后,应用程序将使用相应的appsettings.json文件。您可以在发布后查看所有的 .json 文件,但应用程序仅使用环境设置值。

enter image description here


我在遇到问题时使用了这个,但下一次它就停止工作了。 - Jimit Vaghela
3
我尝试了这个,但在发布时,我只得到了所有3个 appsettings.*.json 文件的副本,我原本期望只有1个会被复制。 - Omu
1
不回答所提出的问题。 - undefined

8
您可以使用MSBuild条件,以便在编译输出(或发布输出)中可选地包含文件。
<ItemGroup Condition="'$(Configuration)'=='Release'">
  <Content Remove="appsettings.Development.json;appsettings.Staging.json" />
  <None Include="appsettings.Development.json;appsettings.Staging.json" />
</ItemGroup>

当编译目标配置为Release时,上述忽略了开发和暂存appsettings.json文件的变体。

3
在我的情况下,我有几个应用于多个实际环境的appSettings文件,例如appSettings.Env1Live.json、appSettings.Env2Live.json等。
我查看了https://learn.microsoft.com/en-us/aspnet/core/host-and-deploy/visual-studio-publish-profiles?view=aspnetcore-3.1文章,并根据每个环境在相应的发布(pubxml)配置文件中添加了以下语句。
例如,对于PublishToEnvironment1.pubxml,添加了:
<ItemGroup>
<Content Update="appsettings.*Live.json" CopyToPublishDirectory="Never" />
<Content Update="appsettings.Development.json" CopyToPublishDirectory="Never" />
<Content Update="appsettings.Env1Live.json" CopyToPublishDirectory="PreserveNewest" /></ItemGroup>

因此,在发布的文件夹中,我只有两个必要的文件,其他的appSettings.*Live.json没有被发布。

appsettings.json

appsettings.Env1Live.json


这个方法可行且简单直接 - 我认为它应该是被采纳的答案。 - jb007

2

在VS2017中添加多个环境

步骤: 右键单击项目 -> 添加 -> 新建项 - 选择json文件 - 将文件名写为'appsettings.staging.json'或'appsettings.production.json'

添加appsettings.staging.json文件

输出结果如下


2
但是只有在本地主机上运行解决方案时才有效。当发布时,无论我在属性(launch.json)中设置什么,环境始终是生产环境:/ 有什么想法吗? - Muflix
@Muflix 你需要将 ASPNETCORE_ENVIRONMENT 变量设置为例如 Staging。它默认为 production。 - Anders Emil
@Anders Emil 实际上,在发布到IIS时,环境变量是从web.config中获取的,而不是从项目的环境属性中获取的。 - Muflix

2

我有同样的问题,因为多个站点使用相同的代码库,需要在不同的生产配置文件之间切换。

最终,我为每个客户/站点创建了一个powershell脚本。该脚本将特定于客户端/站点的配置文件复制到生产配置文件中,然后运行发布。我实际上对appSettings.json文件和environment.ts文件都这样做了。该脚本看起来有点像这样:

Remove-Item –path ClientApp\src\environments\environment.prod.ts
Remove-Item –path appsettings.production.json
Write-Output "--> config files removed"
Copy-Item -path ClientApp\src\environments\environment.CLIENT-SITE-NAME.ts ClientApp\src\environments\environment.prod.ts
Copy-Item -path appsettings.CLIENT-SITE-NAME.json appsettings.production.json
Write-Output "--> config files copied"
dotnet build MYPROJ.csproj -c Release /p:DeployOnBuild=true /p:PublishProfile=CLIENT-SITE-NAME 
Write-Output "--> built & published"
Remove-Item –path ClientApp\src\environments\environment.prod.ts
Remove-Item –path appsettings.production.json
Write-Output "Finished"

在每个客户端站点的 .pubxml 文件中,我排除了所有非生产环境的 appsettings 以便发布,如下所示:
<ItemGroup>
  <Content Update="appsettings.json" CopyToPublishDirectory="Never" />
  <Content Update="appsettings.site1.json" CopyToPublishDirectory="Never" />
  <Content Update="appsettings.site2.json" CopyToPublishDirectory="Never" />
  <Content Update="appsettings.development.json" CopyToPublishDirectory="Never" />
</ItemGroup>

为了确保不会意外将生产文件部署到错误的站点,最后我会删除生产文件,这是使用发布向导的时候需要注意的。

(我将密码存储在pubxml文件中,但您也可以将其作为参数包含在脚本中作为替代方法)


2
考虑您有多个appsettings:dev,pre,prod。 您可以在Web项目文件中使用以下配置。
         <!-- This configuration is done for dotnet publish command.
     It will make sure only environment specific files will be copied -->
          <ItemGroup Condition=" '$(EnvironmentName)' == 'Dev'">
            <Content Remove="appsettings.Prod.json" />
            <Content Remove="appsettings.Pre.json" />
          </ItemGroup>
          <ItemGroup Condition=" '$(EnvironmentName)' == 'Pre'">
            <Content Remove="appsettings.Prod.json" />
            <Content Remove="appsettings.Dev.json" />
            <Content Remove="appsettings.Development.json" />
          </ItemGroup>
          <ItemGroup Condition=" '$(**EnvironmentName**)' == 'Prod'">
            <Content Remove="appsettings.Pre.json" />
            <Content Remove="appsettings.Dev.json" />
            <Content Remove="appsettings.Development.json" />
          </ItemGroup>
        

这个配置将在发布过程中发挥作用。它不会复制不需要的环境设置。
上面突出显示的 EnvironmentName 应该作为下面命令的参数传递。
        dotnet publish -o ../../published/20191118A -c release /p:EnvironmentName=Development

2
您需要根据官方教程添加实际的环境变量
var builder = new ConfigurationBuilder()
    .SetBasePath(env.ContentRootPath)
    .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: false, reloadOnChange: true)
    // do not forget to add environment variables to your config!
    .AddEnvironmentVariables();

2
我在.csproj中尝试了以下配置。 我有三个应用程序json文件,分别针对每个环境(开发、暂存、生产)。我已根据环境变量更新了.csproj文件。
<ItemGroup Condition="'$(EnvironmentName)' == 'Development'">
    <Content Update="appsettings.Production.json" 
CopyToPublishDirectory="Never"/>
    <Content Update="appsettings.Staging.json" 
CopyToPublishDirectory="Never"/>
</ItemGroup>

<ItemGroup Condition="'$(EnvironmentName)' == 'Staging'">
    <Content Update="appsettings.Production.json" 
CopyToPublishDirectory="Never"/>
    <Content Update="appsettings.Development.json" 
CopyToPublishDirectory="Never"/>
</ItemGroup>

<ItemGroup Condition="'$(EnvironmentName)' == 'Production'">
    <Content Update="appsettings.Staging.json" 
CopyToPublishDirectory="Never"/>
    <Content Update="appsettings.Development.json" 
CopyToPublishDirectory="Never"/>
</ItemGroup>

希望这对每个人都有效。

迄今为止最简单的解决方案。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接