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

这里有一个可用的解决方案:对App.Config应用XDT魔术。

然而,这并不简单,需要一些步骤。是否有更简单的方法来实现同样的app.config文件?


我创建了Vishal Joshi发布的另一个替代方案,其中将构建动作更改为内容的需求被删除,并实现了对ClickOnce部署的基本支持。我说基本,是因为我没有彻底测试它,但它应该在典型的ClickOnce部署场景中工作。

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

你可以在Visual Studio App.config XML Transformation上阅读更详细的解释,MSBuild项目文件可以从GitHub下载。

根据我的经验,我需要使环境特定的东西是像连接字符串、appsettings和通常的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)。

我尝试了几种解决方案,下面是我个人发现的最简单的一个。 Dan在评论中指出,原来的帖子属于Oleg sych -谢谢,Oleg!

以下是使用说明:

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

通常你会有调试和发布配置,所以把你的文件命名为App.Debug.config和App.Release.config。在我的项目中,我为每种环境创建了一个配置,所以您可能想尝试一下。

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

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

3.绑定程序。*。配置文件到主App.config

找到包含所有App.config和App.*的项目文件部分。配置引用。你会注意到它们的构建动作被设置为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转换!

FYI

确保你的App.*。配置文件有如下正确的设置:

<?xml version="1.0" encoding="utf-8"?>
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
     <!--magic transformations here-->
</configuration>

这现在可以与本文中处理的Visual Studio AddIn一起工作:SlowCheetah - Web。config转换语法现在适用于任何XML配置文件。

You can right-click on your web.config and click "Add Config Transforms." When you do this, you'll get a web.debug.config and a web.release.config. You can make a web.whatever.config if you like, as long as the name lines up with a configuration profile. These files are just the changes you want made, not a complete copy of your web.config. You might think you'd want to use XSLT to transform a web.config, but while they feels intuitively right it's actually very verbose. Here's two transforms, one using XSLT and the same one using the XML Document Transform syntax/namespace. As with all things there's multiple ways in XSLT to do this, but you get the general idea. XSLT is a generalized tree transformation language, while this deployment one is optimized for a specific subset of common scenarios. But, the cool part is that each XDT transform is a .NET plugin, so you can make your own. <?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> Or the same thing via the deployment transform: <configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform"> <appSettings> <add name="NewSetting" value="New Setting Value" xdt:Transform="Insert"/> </appSettings> </configuration>

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

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

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

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

这个扩展的最大优势是,你不需要在所有的构建机器上安装它

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

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

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

只是对现在到处张贴的解决方案做了一点改进:

<UsingTask TaskName="TransformXml" AssemblyFile="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Web\Microsoft.Web.Publishing.Tasks.dll" />

也就是说,除非你打算永远使用当前的VS版本

受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文件添加到项目中。 通常你会有调试和发布配置,所以把你的文件命名为App.Debug.config和App.Release.config。在我的项目中,我为每种环境创建了一个配置,所以您可能想尝试一下。 2. 卸载项目并打开.csproj文件进行编辑 Visual Studio允许您在编辑器中编辑.csproj—您只需要首先卸载项目。然后右键单击它并选择Edit .csproj。

3.绑定程序。*。配置文件到主App.config

找到包含所有App.config和App.*的项目文件部分。配置引用和替换如下所示。您将注意到我们使用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. 激活转换魔法 在文件结束后 <进口项目= " $ (MSBuildToolsPath) \ Microsoft.CSharp。目标" / > 在期末考试之前 > < /项目

插入以下XML文件:

<Import Project="AppConfigTransformation.targets" />

完成了!

So I ended up taking a slightly different approach. I followed Dan's steps through step 3, but added another file: App.Base.Config. This file contains the configuration settings you want in every generated App.Config. Then I use BeforeBuild (with Yuri's addition to TransformXml) to transform the current configuration with the Base config into the App.config. The build process then uses the transformed App.config as normal. However, one annoyance is you kind of want to exclude the ever-changing App.config from source control afterwards, but the other config files are now dependent upon it.

  <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>

如果你使用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在线(云)服务器。

建议的解决方案将不工作时,类库配置文件引用从另一个项目(在我的情况下,它是Azure工作项目库)。它不会将正确转换后的文件从obj文件夹复制到bin\##configuration-name##文件夹。为了使它在最小的变化下工作,你需要将AfterCompile target改为BeforeCompile:

<Target Name="BeforeCompile" Condition="exists('app.$(Configuration).config')">

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

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

注意:由于声誉,我不能评论bdeem的帖子。我将把我的发现作为答案发表出来。

根据bdeem的帖子,我做了以下(按顺序):

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

注意:使用<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. 在[项目]的底部。csproj文件(在关闭</Project>标记之前),我导入了${MSBuildToolsPath\Microsoft.CSharp。添加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.*.;配置文件具有适当的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. 重建项目。

这是@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>

它还有一个额外的好处,就是比原来的解决方案要短得多。