571

对于 Visual Studio 2010 基于 Web 的应用程序,我们具有配置转换功能,通过这些功能我们可以为不同的环境维护多个配置文件。但相同的功能不适用于 Windows 服务/WinForms 或控制台应用程序的 App.Config 文件。

此处建议有一种解决方法:将 XDT 魔法应用于 App.Config

然而,这并不简单,需要许多步骤。有没有更简单的方法来实现 app.config 文件的相同功能?

4

15 回答 15

618

我尝试了几种解决方案,这是我个人发现的最简单的。
Dan在评论中指出原始帖子属于Oleg Sych —<strong>谢谢,Oleg!

以下是说明:

1. 将每个配置的 XML 文件添加到项目中。

通常,您将拥有DebugRelease配置,因此命名您的文件App.Debug.configApp.Release.config. 在我的项目中,我为每种环境创建了一个配置,因此您可能想尝试一下。

2.卸载项目并打开.csproj文件进行编辑

Visual Studio 允许您直接在编辑器中编辑.csproj文件——您只需先卸载项目。然后右键单击它并选择Edit <ProjectName>.csproj

3. 将 App.*.config 文件绑定到主 App.config

找到包含所有App.configApp.*.config引用的项目文件部分。您会注意到他们的构建操作设置为None,这没关系:

<None Include="App.config" />
<None Include="App.Debug.config" />
<None Include="App.Release.config" />

接下来,使所有特定于配置的文件都依赖于主文件,App.config以便 Visual Studio 像对设计器和代码隐藏文件一样对它们进行分组。

将上面的 XML 替换为下面的 XML:

<None Include="App.config" />
<None Include="App.Debug.config" >
  <DependentUpon>App.config</DependentUpon>
</None>
<None Include="App.Release.config" >
  <DependentUpon>App.config</DependentUpon>
</None>

4.激活转换魔法( VS2019等Visual Studio版本仍然需要)

在文件末尾之后

<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />

在决赛之前

</Project>

插入以下 XML - 请注意,正确转换需要两个步骤:

  <UsingTask TaskName="TransformXml" AssemblyFile="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Web\Microsoft.Web.Publishing.Tasks.dll" />
  <Target Name="BeforeBuild" Condition="Exists('App.$(Configuration).config')">
    <!-- Generate transformed app config and replace it: will get the <runtime> node and assembly bindings properly populated -->
    <TransformXml Source="App.config" Destination="App.config" Transform="App.$(Configuration).config" />
  </Target>
  <Target Name="AfterBuild" Condition="Exists('App.$(Configuration).config')">
    <!-- Generate transformed app config in the intermediate directory: this will transform sections such as appSettings -->
    <TransformXml Source="App.config" Destination="$(IntermediateOutputPath)$(TargetFileName).config" Transform="App.$(Configuration).config" />
    <!-- Force build process to use the transformed configuration file from now on.-->
    <ItemGroup>
      <AppConfigWithTargetPath Remove="App.config" />
      <AppConfigWithTargetPath Include="$(IntermediateOutputPath)$(TargetFileName).config">
        <TargetPath>$(TargetFileName).config</TargetPath>
      </AppConfigWithTargetPath>
    </ItemGroup>
  </Target>

现在您可以重新加载项目、构建它并享受App.config转换!

供参考

确保您的App.*.config文件具有正确的设置,如下所示:

<?xml version="1.0" encoding="utf-8"?>
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
     <!--magic transformations here-->
</configuration>
于 2011-02-24T19:36:27.873 回答
421

这现在适用于本文中介绍的 Visual Studio 插件:SlowCheetah - Web.config Transformation Syntax now generalized for any XML configuration file

您可以右键单击您的 web.config,然后单击“添加配置转换”。当你这样做时,你会得到一个 web.debug.config 和一个 web.release.config。如果您愿意,您可以创建一个 web.whatever.config,只要名称与配置文件一致。这些文件只是您想要进行的更改,而不是您的 web.config 的完整副本。

您可能认为您想使用 XSLT 来转换 web.config,但是虽然它们直观地感觉是正确的,但实际上非常冗长。

这里有两种转换,一种使用 XSLT,另一种使用 XML 文档转换语法/命名空间。与所有事情一样,在 XSLT 中有多种方法可以做到这一点,但您会明白大致的想法。XSLT 是一种通用的树转换语言,而这种部署语言针对常见场景的特定子集进行了优化。但是,很酷的部分是每个 XDT 转换都是一个 .NET 插件,因此您可以自己制作。

<?xml version="1.0" ?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="@*|node()">
  <xsl:copy>           
    <xsl:apply-templates select="@*|node()"/>
  </xsl:copy>
</xsl:template>
<xsl:template match="/configuration/appSettings">
  <xsl:copy>
    <xsl:apply-templates select="node()|@*"/>
    <xsl:element name="add">
      <xsl:attribute name="key">NewSetting</xsl:attribute>
      <xsl:attribute name="value">New Setting Value</xsl:attribute>
    </xsl:element>
  </xsl:copy>
</xsl:template>
</xsl:stylesheet>

或者通过部署转换同样的事情:

<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
   <appSettings>
      <add name="NewSetting" value="New Setting Value" xdt:Transform="Insert"/>
   </appSettings>
</configuration>
于 2011-08-25T18:15:22.723 回答
143

我发现的另一个解决方案是不使用转换,而只使用一个单独的配置文件,例如 app.Release.config。然后将此行添加到您的 csproj 文件中。

  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
    <AppConfig>App.Release.config</AppConfig>
  </PropertyGroup>

这不仅会生成正确的 myprogram.exe.config 文件,而且如果您在 Visual Studio 中使用安装和部署项目来生成 MSI,它将强制部署项目在打包时使用正确的配置文件。

于 2011-10-18T18:12:55.087 回答
37

Oleg和其他人在这个问题中的启发,我将解决方案https://stackoverflow.com/a/5109530/2286801更进一步以启用以下功能。

  • 与 ClickOnce 一起使用
  • 适用于 VS 2010 中的安装和部署项目
  • 适用于 VS2010、2013、2015(未测试 2012 虽然应该也可以)。
  • 与团队建设一起使用。(您必须安装 A)Visual Studio 或 B)Microsoft.Web.Publishing.targets 和 Microsoft.Web.Publishing.Tasks.dll)

此解决方案通过在 MSBuild 过程中首次引用 app.config 之前执行 app.config 转换来工作。它使用外部目标文件来更轻松地跨多个项目进行管理。

指示:

与其他解决方案类似的步骤。我引用了保持不变的内容并将其包括在内以保持完整性和更容易比较。

0. 向您的项目添加一个名为 AppConfigTransformation.targets 的新文件

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <!-- Transform the app config per project configuration.-->
  <PropertyGroup>
    <!-- This ensures compatibility across multiple versions of Visual Studio when using a solution file.
         However, when using MSBuild directly you may need to override this property to 11.0 or 12.0 
         accordingly as part of the MSBuild script, ie /p:VisualStudioVersion=11.0;
         See http://blogs.msdn.com/b/webdev/archive/2012/08/22/visual-studio-project-compatability-and-visualstudioversion.aspx -->
    <VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
  </PropertyGroup>

  <Import Project="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Web\Microsoft.Web.Publishing.targets" />

  <Target Name="SetTransformAppConfigDestination" BeforeTargets="PrepareForBuild" 
          Condition="exists('app.$(Configuration).config')">
    <PropertyGroup>
      <!-- Force build process to use the transformed configuration file from now on. -->
      <AppConfig>$(IntermediateOutputPath)$(TargetFileName).config</AppConfig>
    </PropertyGroup>
    <Message Text="AppConfig transformation destination: = $(AppConfig)" />
  </Target>

  <!-- Transform the app.config after the prepare for build completes. -->
  <Target Name="TransformAppConfig" AfterTargets="PrepareForBuild" Condition="exists('app.$(Configuration).config')">
    <!-- Generate transformed app config in the intermediate directory -->
    <TransformXml Source="app.config" Destination="$(AppConfig)" Transform="app.$(Configuration).config" />
  </Target>

</Project>

1. 将每个配置的 XML 文件添加到项目中。

通常,您将拥有 Debug 和 Release 配置,因此将文件命名为 App.Debug.config 和 App.Release.config。在我的项目中,我为每种环境创建了一个配置,因此您可能想尝试一下。

2.卸载项目并打开.csproj文件进行编辑

Visual Studio 允许您直接在编辑器中编辑 .csproj — 您只需先卸载项目。然后右键单击它并选择编辑 .csproj。

3. 将 App.*.config 文件绑定到主 App.config

找到包含所有 App.config 和 App.*.config 引用的项目文件部分并替换如下。您会注意到我们使用 None 而不是 Content。

<ItemGroup>
  <None Include="app.config"/>
  <None Include="app.Production.config">
    <DependentUpon>app.config</DependentUpon>
  </None>
  <None Include="app.QA.config">
    <DependentUpon>app.config</DependentUpon>
  </None>
  <None Include="app.Development.config">
    <DependentUpon>app.config</DependentUpon>
  </None>
</ItemGroup>

4.激活变形魔法

在文件末尾之后

<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />

在决赛之前

</Project>

插入以下 XML:

<Import Project="AppConfigTransformation.targets" />

完毕!

于 2014-10-01T23:23:18.457 回答
35

根据我的经验,我需要使环境特定的东西是连接字符串、应用程序设置和通常的 smpt 设置。配置系统允许在单独的文件中指定这些内容。所以你可以在你的 app.config/web.config 中使用它:

 <appSettings configSource="appsettings.config" />
 <connectionStrings configSource="connection.config" />
 <system.net>
    <mailSettings>
       <smtp configSource="smtp.config"/>
    </mailSettings>
 </system.net>

我通常做的是将这些特定于配置的部分放在单独的文件中,放在名为 ConfigFiles 的子文件夹中(在解决方案根目录或项目级别,取决于)。我为每个配置定义一个文件,例如 smtp.config.Debug 和 smtp.config.Release。

然后你可以像这样定义一个预构建事件:

copy $(ProjectDir)ConfigFiles\smtp.config.$(ConfigurationName) $(TargetDir)smtp.config

在团队开发中,您可以通过在约定中包含 %COMPUTERNAME% 和/或 %USERNAME% 来进一步调整。

当然,这意味着不应将目标文件 (x.config) 放在源代码管理中(因为它们是生成的)。您仍然应该将它们添加到项目文件中,并将它们的输出类型属性设置为“始终复制”或“如果更新则复制”。

简单、可扩展,并且适用于所有类型的 Visual Studio 项目(控制台、winforms、wpf、web)。

于 2011-02-12T10:08:49.110 回答
29

您可以为每个配置使用单独的配置文件,例如 app.Debug.config、app.Release.config,然后在项目文件中使用配置变量:

<PropertyGroup>
    <AppConfig>App.$(Configuration).config</AppConfig>
</PropertyGroup>

然后,这将根据您正在构建的配置创建正确的 ProjectName.exe.config 文件。

于 2012-07-02T09:32:53.183 回答
14

我写了一个很好的扩展来自动化 app.config 转换,就像内置的 Web 应用程序项目配置转换一样

这个扩展最大的好处是你不需要在所有的构建机器上都安装它

于 2012-05-27T07:59:44.077 回答
6

从 Marketplace 在 Visual Studio 中安装“配置转换工具”并重新启动 VS。您还可以看到 app.config 的菜单预览转换。

https://marketplace.visualstudio.com/items?itemName=GolanAvraham.ConfigurationTransform

于 2018-02-13T17:19:39.637 回答
4

只是对现在似乎到处都发布的解决方案进行了一点改进:

<UsingTask TaskName="TransformXml" AssemblyFile="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Web\Microsoft.Web.Publishing.Tasks.dll" />
  • 也就是说,除非您打算永远使用当前的 VS 版本
于 2013-08-06T08:34:53.290 回答
4

所以我最终采取了一种稍微不同的方法。我按照 Dan 的步骤完成了第 3 步,但添加了另一个文件:App.Base.Config。此文件包含您希望在每个生成的 App.Config 中的配置设置。然后我使用BeforeBuild(将Yuri 添加到TransformXml)将带有Base config 的当前配置转换为App.config。然后,构建过程照常使用转换后的 App.config。然而,一个烦恼是你有点想从源代码控制中排除不断变化的 App.config,但其他配置文件现在依赖于它。

  <UsingTask TaskName="TransformXml" AssemblyFile="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Web\Microsoft.Web.Publishing.Tasks.dll" />
  <Target Name="BeforeBuild" Condition="exists('app.$(Configuration).config')">
    <TransformXml Source="App.Base.config" Transform="App.$(Configuration).config" Destination="App.config" />
  </Target>
于 2014-10-29T22:05:30.303 回答
2

我创建了 Vishal Joshi 发布的另一种替代方法,其中删除了将构建操作更改为Content的要求,并且还实现了对 ClickOnce 部署的基本支持。我说的是基本的,因为我没有彻底测试它,但它应该可以在典型的 ClickOnce 部署场景中工作。

该解决方案由单个 MSBuild 项目组成,该项目一旦导入现有的 Windows 应用程序项目 (*.csproj),就会扩展构建过程以考虑 app.config 转换。

您可以在Visual Studio App.config XML 转换中阅读更详细的说明,并且可以从 GitHub 下载MSBuild 项目文件。

于 2010-06-22T15:59:42.240 回答
1

如果您使用在线 TFS(云版),并且您想在项目中转换 App.Config,您可以在不安装任何额外工具的情况下执行以下操作。从 VS => 卸载项目 => 编辑项目文件 => 转到文件底部并添加以下内容:

<UsingTask TaskName="TransformXml" AssemblyFile="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Web\Microsoft.Web.Publishing.Tasks.dll" />
<Target Name="AfterBuild" Condition="Exists('App.$(Configuration).config')">
<TransformXml Source="App.config" Transform="App.$(Configuration).config" Destination="$(OutDir)\$(AssemblyName).dll.config" />

AssemblyFile 和 Destination 适用于本地使用和 TFS 在线(云)服务器。

于 2015-03-18T09:28:03.017 回答
1

当从另一个项目(在我的情况下是 Azure 工作项目库)引用带有配置文件的类库时,建议的解决方案将不起作用。它不会将正确的转换文件从obj文件夹复制到bin\##configuration-name##文件夹中。要使其以最小的更改工作,您需要将AfterCompile目标更改为BeforeCompile

<Target Name="BeforeCompile" Condition="exists('app.$(Configuration).config')">
于 2016-10-06T18:57:37.083 回答
1

注意:由于声誉问题,我无法对bdeem帖子发表评论。我将我的发现作为答案发布。

bdeem帖子之后,我做了以下(按顺序):

1.我修改了[project].csproj文件。为不同的文件添加了<Content Include="" />标签,并使它们依赖于原始文件。ItemGroupconfigconfig

注意:使用<None Include="" />不适用于转换。

<!-- App.config Settings -->
<!-- Create App.($Configuration).config files here. -->
<Content Include="App.config" />
<Content Include="App.Debug.config">
  <DependentUpon>App.config</DependentUpon>
</Content>
<Content Include="App.Release.config">
  <DependentUpon>App.config</DependentUpon>
</Content>

2. 在[project].csproj文件的底部(结束</Project>标记之前),我导入了${MSBuildToolsPath\Microsoft.CSharp.targets文件,添加了UsingTask以转换 XML 并添加了Target以将转换后的文件复制App.config到输出位置。

注意:Target还将覆盖App.Config本地目录中的 ,以查看本地工作的即时更改。Target还使用该属性来确保在生成可执行文件Name="Afterbuild"可以转换配置文件。由于我不明白的原因,在使用 WCF 端点时,如果我使用,我会收到有关服务属性的警告。解决了这个问题。Name="CoreCompile"Name="Afterbuild"

  <!-- Task to transform the App.config using the App.($Configuration).config file. -->
  <UsingTask TaskName="TransformXml" AssemblyFile="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Web\Microsoft.Web.Publishing.Tasks.dll" />

  <!-- Only compile the App.config if the App.($Configuration).config file exists. -->
  <!-- Make sure to use the AfterBuild name instead of CoreCompile to avoid first time build errors and WCF endpoint errors. -->
  <Target Name="AfterBuild" Condition="exists('App.$(Configuration).config')">
    <!-- Generate transformed App.config in the intermediate output directory -->    
    <TransformXml Source="App.config" Destination="$(IntermediateOutputPath)$(TargetFileName).config" Transform="App.$(Configuration).config" />
    
    <!-- Modify the original App.config file with the transformed version. -->
    <TransformXml Source="App.config" Destination="App.config" Transform="App.$(Configuration).config" />

    <!-- Force build process to use the transformed configuration file from now on. -->
    <ItemGroup>
      <AppConfigWithTargetPath Remove="App.config" />
      <AppConfigWithTargetPath Include="$(IntermediateOutputPath)$(TargetFileName).config">
        <TargetPath>$(TargetFileName).config</TargetPath>
      </AppConfigWithTargetPath>
    </ItemGroup>
  </Target>
</Project>

3. 回到 Visual Studio 并重新加载修改后的文件。

4. 手动将App.*.config文件添加到项目中。这允许他们在原始App.config文件下分组。

注意:确保App.*.config文件具有正确的 XML 结构。

<?xml version="1.0" encoding="utf-8"?>

<!-- For more information on using web.config transformation visit https://go.microsoft.com/fwlink/?LinkId=125889 -->

<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
  <connectionStrings>
    <add name="myConn" connectionString=""; Initial Catalog=; User ID=; Password=;" xdt:Transform="SetAttributes" xdt:Locator="Match(name)" />
  </connectionStrings>
</configuration>

5. 重新构建项目。

于 2020-11-04T16:38:09.267 回答
0

@bdeem 使用 Visual Studio 2019 和 2022 的答案的另一个变体。我的问题是使用该解决方案App.config被覆盖,并且由于它在源代码控制中,这并不是一个真正的选择。

我的解决方案是将配置文件直接转换到输出目录中。

  <UsingTask TaskName="TransformXml" AssemblyFile="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Web\Microsoft.Web.Publishing.Tasks.dll" />
  <Target Name="AfterBuild" Condition="Exists('App.$(Configuration).config')">
    <!-- Generate transformed app config to the output directory -->
    <TransformXml Source="App.config" Destination="$(OutDir)\$(TargetFileName).config" Transform="App.$(Configuration).config" />
  </Target>

它的另一个好处是比原始解决方案短得多。

于 2022-02-04T17:01:15.320 回答