Web Config Transform不起作用。

104

在一个.NET MVC 3.0应用程序中,我有以下配置在appSettings中:

web.config

<appSettings>
<add key="SMTPHost" value="mail.domain.com"/>
    <add key="SMTPUsername" value="user@gmail.com"/>
    <add key="SMTPPort" value="25"/>
    <add key="SMTPPwd" value="mypassword"/>
    <add key="EmailFrom" value="notific@gmail.com"/>
</appSettings>

为了调试,我定义了以下配置转换:

web.Debug.config

<appSettings>
    <add  key="SMTPPort" value="58" xdt:Transform="Replace" xdt:Locator="Match(key)" />
</appSettings>

我在调试模式下运行应用程序,但我的SMTP端口仍然使用web.config中的值,而不是web.Debug.config中的值。

有人可以建议这个配置有什么问题吗?

10个回答

173

1
非常感谢你,你为我节省了很多时间。 - HaBo
3
哇,我花了2个小时才找到这个答案。谢谢你的发布,否则我就要抓狂了。 - Peanut
在 Visual Studio 2015 中,**(Web).config transforms** 现在是一个内置的功能,看起来你不再需要使用 SlowCheetah。但是只有在发布应用程序时,内置转换才会生效,而不是在运行时。你可以在 这里 查看我是如何解决它的。 - Matt
1
不确定为什么要使用这个,@komsky的答案提供了一个简单而干净的解决方案。 - Csaba Toth
2
SlowCheetah很棒,但根据他们自己的文档:“对于Web项目,文件在发布或打包应用程序时进行转换。”换句话说,在调试期间不会进行转换。 - Doug

37
很不幸,Visual Studio (2010 - 2019)在调试时并不直接支持它,它只用于发布 - 即使使用SlowCheetah扩展(标记答案),对于我来说也不起作用(仅适用于使用app.config而非web.config的项目)。
请注意,在codeproject上有一个解决方法
它描述了如何修改.msproj文件以通过转换后的版本覆盖当前的web.config。
我首先将该解决方法描述为选项1,但我最近发现了另一个更易于使用的选项2(因此,如果您喜欢,可以直接滚动到选项2):
选项1:我添加了从原始codeproject文章中获取的说明(请参见上面的链接),因为那里的截图已经消失了,我不想失去整个信息:
当您正在开发和调试本地环境时,VS.Net不会进行任何转换。但是,如果您想要这样做,可以执行以下一些步骤。
  • 首先,在VS.Net中创建您想要的配置,假设默认的调试和发布不足以实现您想要完成的任务。
  • 右键单击您的web.config并选择添加配置转换 - 这将为您定义的每个配置创建一个依赖转换配置。
  • 现在,您可以将web.config重命名为web.base.config
  • 向项目中添加一个web.config。它里面放什么都无所谓,因为每次构建时都会被覆盖,但我们希望它成为项目的一部分,以便VS.Net不会给我们弹出"您的项目未配置为调试"的弹窗。
  • 编辑您的.csproj项目文件并将以下TransformXml任务添加到AfterBuild目标中。在这里,您可以看到我将使用web.[configuration].config转换web.base.config文件,并将其保存为web.config。有关详细信息,请查看此Microsoft Q&A,有关如何扩展构建的说明,请查看此处

选项2:

基于这个答案,我开发了一个简单的控制台应用程序TransformConfig.exe(使用C# 6.0语法):

using System;
using System.Linq;
using Microsoft.Web.XmlTransform;

namespace TransformConfig
{

  class Program
  {
    static int Main(string[] args)
    {
        var myDocumentsFolder = $@"C:\Users\{Environment.UserName}\Documents";
        var myVsProjects = $@"{myDocumentsFolder}\Visual Studio 2015\Projects";

        string srcConfigFileName = "Web.config";
        string tgtConfigFileName = srcConfigFileName;
        string transformFileName = "Web.Debug.config";
        string basePath = myVsProjects + @"\";
        try
        {

            var numArgs = args?.Count() ?? 0;
            if (numArgs == 0 || args.Any(x=>x=="/?"))
            {
                Console.WriteLine("\nTransformConfig - Usage:");
                Console.WriteLine("\tTransformConfig.exe /d:tgtConfigFileName [/t:transformFileName [/s:srcConfigFileName][/b:basePath]]");
                Console.WriteLine($"\nIf 'basePath' is just a directory name, '{basePath}' is preceeded.");
                Console.WriteLine("\nTransformConfig - Example (inside PostBuild event):");
                Console.WriteLine("\t\"c:\\Tools\\TransformConfig.exe\"  /d:Web.config /t:Web.$(ConfigurationName).config /s:Web.Template.config /b:\"$(ProjectDir)\\\"");
                Environment.ExitCode = 1;
                return 1;
            }

            foreach (var a in args)
            {
                var param = a.Trim().Substring(3).TrimStart();
                switch (a.TrimStart().Substring(0,2).ToLowerInvariant())
                {
                    case "/d":
                        tgtConfigFileName = param ?? tgtConfigFileName;
                        break;
                    case "/t":
                        transformFileName = param ?? transformFileName;
                        break;
                    case "/b":
                        var isPath = (param ?? "").Contains("\\");
                        basePath = (isPath == false)
                                    ? $@"{myVsProjects}\" + param ?? ""
                                    : param;
                        break;
                    case "/s":
                        srcConfigFileName = param ?? srcConfigFileName;
                        break;
                    default:
                        break;
                }
            }
            basePath = System.IO.Path.GetFullPath(basePath);
            if (!basePath.EndsWith("\\")) basePath += "\\";
            if (tgtConfigFileName != srcConfigFileName)
            {
                System.IO.File.Copy(basePath + srcConfigFileName,
                                     basePath + tgtConfigFileName, true);
            }
            TransformConfig(basePath + tgtConfigFileName, basePath + transformFileName);
            Console.WriteLine($"TransformConfig - transformed '{basePath + tgtConfigFileName}' successfully using '{transformFileName}'.");
            Environment.ExitCode = 0;
            return 0;
        }
        catch (Exception ex)
        {
            var msg = $"{ex.Message}\nParameters:\n/d:{tgtConfigFileName}\n/t:{transformFileName}\n/s:{srcConfigFileName}\n/b:{basePath}";
            Console.WriteLine($"TransformConfig - Exception occurred: {msg}");
            Console.WriteLine($"TransformConfig - Processing aborted.");
            Environment.ExitCode = 2;
            return 2;
        }
    }

    public static void TransformConfig(string configFileName, string transformFileName)
    {
        var document = new XmlTransformableDocument();
        document.PreserveWhitespace = true;
        document.Load(configFileName);

        var transformation = new XmlTransformation(transformFileName);
        if (!transformation.Apply(document))
        {
            throw new Exception("Transformation Failed");
        }
        document.Save(configFileName);
    }

  }
}

请确保将DLL "C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\v14.0\Web\Microsoft.Web.XmlTransform.dll" 添加为引用(此示例适用于VS 2015,对于旧版,请将路径中的 v14.0 替换为相应的版本号,例如 v11.0)。
对于 Visual Studio 2017 ,路径的命名方案已更改:例如,对于企业版,它在这里:C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\MSBuild\Microsoft\VisualStudio\v15.0\Web
我假设对于专业版,您需要在路径中用 Professional 替换 Enterprise。如果您使用预览版,则还需要将 2017 替换为 Preview
以下是不同版本的Visual Studio中路径的概述(如果您没有企业版,则可能需要在路径中用 Professional 替换 Enterprise):

VS 版本        路径(对于 Microsoft.Web.XmlTransform.dll
2015                  C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\v14.0\Web
2017                  C:\Program Files (x86)\Microsoft Visual Studio\2017\
                          Enterprise\MSBuild\Microsoft\VisualStudio\v15.0\Web
2019                  C:\Program Files (x86)\Microsoft Visual Studio\2019\
                          Enterprise\MSBuild\Microsoft\VisualStudio\v16.0\Web

编译它并将 .exe 文件放入一个目录中,例如 C:\MyTools\用法: 您可以在项目属性中的构建事件中使用它(选择构建事件,然后编辑后期构建事件命令行)。命令行参数如下(示例):

"C:\MyTools\TransformConfig.Exe" /d:Web.config /t:Web.$(ConfigurationName).config /s:Web.Template.config /b:"$(ProjectDir)\"

即首先是配置文件名称,然后是变换配置文件,然后是可选模板配置,最后是包含这两个文件的项目的路径。
我添加了可选的模板配置参数,因为否则,您的原始完整配置将被变换覆盖,这可以通过提供模板来避免。

通过简单地复制原始的Web.config并将其命名为Web.Template.config来创建模板。

注意:

  • 如果您愿意,您也可以将TransformConfig.exe文件复制到上述提到的Visual Studio路径中,其中Microsoft.Web.XmlTransform.dll存放,并在需要转换配置的所有项目中引用它。

  • 对于那些想知道为什么我添加了“Environment.ExitCode = x;”赋值的人:只是从Main返回一个int在构建事件中没有帮助。详细信息请参见此处

  • 如果您正在发布项目并且正在使用Web.Template.config,请确保在发布之前使用正确的配置(通常是Release)对解决方案进行了重新生成。原因是在调试期间会覆盖Web.Config,否则可能会转换错误的文件。


1
看起来CodeProject的帖子出了问题。他在代码示例中使用了屏幕截图,现在由于他的博客已经关闭,它们已经失传了。 - Eric Lloyd
3
很遗憾,截图已经丢失了。但至少文章文本还在,描述了方法。为避免丢失,我已将文本描述添加到答案中。 - Matt
1
确实,也许可以尝试联系CodeProject的作者James Coleman,在那里修复它。不过我不确定他是否仍然活跃在那里。@ThomasTeilmann - Matt
我认为这可能与丢失的截图中的内容相似。似乎实现了相同的基本结果。https://dev59.com/fGw15IYBdhLWcg3wuuGn#6437192 - cowsay
您可以通过web.archive访问带有屏幕截图的链接:https://web.archive.org/web/20120728000945/http://ledtalks.posterous.com/webconfig-transformations-when-debugging - Adam
显示剩余2条评论

28
回答你的问题并不简单,因为它提出了一个问题 - 如果你想用Web.debug.config转换Web.config - 转换效果应该存储在哪里?在Web.config本身中吗?这将覆盖转换源文件!可能这就是为什么Visual Studio在构建过程中不进行转换的原因。
之前Matt的回答是有效的,但你可能希望将它们混合起来,以获得通用解决方案,当你实际上从调试切换到发布等活动解决方案配置时,它可以正常工作。以下是一个简单的解决方案:
1. 为配置(Debug、Release等)创建你的配置转换。 2. 将Web.config文件重命名为Web.base.config - 转换应自动重命名(Web.base.Debug.config等)。 3. 在你的项目文件夹中添加以下transformWebConfig.proj XML文件:
<?xml version="1.0" encoding="utf-8" ?>
<Project ToolsVersion="4.0" DefaultTargets="TransformWebConfig" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

  <UsingTask TaskName="TransformXml" AssemblyFile="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v12.0\Web\Microsoft.Web.Publishing.Tasks.dll" />
  <Target Name="TransformWebConfig">
    <TransformXml Source="Web.base.config" Transform="Web.base.$(CurrentConfig).config" Destination="Web.config" />
  </Target>
</Project>

导航到您的项目属性,选择构建事件并将以下内容添加到后期构建事件命令行
@if exist "%ProgramFiles(x86)%\MSBuild\12.0\bin" set PATH=%ProgramFiles(x86)%\MSBuild\12.0\bin;%PATH%
msbuild $(ProjectDir)transformWebConfig.proj /t:TransformWebConfig /p:CurrentConfig=$(ConfigurationName) /p:TargetProjectName=$(TargetPath)

现在,当您构建解决方案时,将创建一个包含活动配置的有效转换的Web.config文件。

  1. 我们无法确定VS如何使用模式验证此XML,但这个警告很常见,所以您可以忽略它。
  2. 这取决于您使用的仓库,但您可以例如使用git.ignore文件条目。
- komsky
5
这对我很有效--只需将构建事件和proj文件中的12更改为当前版本。对于后期构建事件,我使用: '"$(MSBuildBinPath)\msbuild.exe" $(ProjectDir)TransformWebConfig.proj /t:TransformWebConfig /p:CurrentConfig=$(ConfigurationName) /p:TargetProjectName=$(TargetPath)并在.proj文件中更新了v12.0v14.0 - Dana
1
对于VS 2017,将每个“12.0”修改为“14.0”。 - Csaba Toth
1
如果您的项目包含空格,则需要用""将$(ProjectDir)括起来。@if存在"%ProgramFiles(x86)%\MSBuild\14.0\bin",则设置PATH=%ProgramFiles(x86)%\MSBuild\14.0\bin;%PATH% msbuild "$(ProjectDir)transformWebConfig.proj" /t:TransformWebConfig /p:CurrentConfig=$(ConfigurationName) /p:TargetProjectName="$(TargetPath)" - The One
1
  1. 别忘了将生成的 web.config 包含到 Web 项目中,否则在发布后它不会被复制到目标文件夹中。
  2. 如果构建服务器缺少这两个文件,只需将它们复制到服务器上的 "Microsoft.Web.Publishing.Tasks" 和 "Microsoft.Web.XmlTransform"。
- phiree
显示剩余6条评论

8
对于VS 2017,我在这里找到了答案(链接),不确定为什么之前没有人引用它,因为它似乎是一个非常受欢迎的解决方案。而且非常容易。确保您看到IOrlandoni在2019年3月5日发表的评论,以使其在VS 2017和所有版本中正常工作。
基本上,它是一个两步骤的过程。首先,您需要编辑.csproj文件,附加下面的代码。其次,您需要创建一个名为web.base.config的新的配置文件,并将现有的web.config复制到其中。完成以上步骤后,任何构建都会使用您所需的转换覆盖您的web.config文件。
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\WebApplications\Microsoft.WebApplication.targets" />
<Target Name="BeforeBuild">
    <TransformXml Source="Web.Base.config" 
        Transform="Web.$(Configuration).config" Destination="Web.config" />
</Target>  

这可能是最好的答案,但在我看来它还缺少了一个技巧。如果你将 Web.configContent 改为 None,那么你就可以使用 Source="Web.config" Destination="$(TargetPath).config"(或者对于某些类型的项目,Destination="$(TargetDir)Web.config")。我还将转换移到了 AfterBuild,因为它不再需要在文件复制之前完成。 - Peter Taylor
好的,实际上这是行不通的,因为由于某种原因我无法配置它从bin运行。 - Peter Taylor

6
你的直接问题已经得到了回答 - 解释是变换应用于发布,而不是构建。
然而,我认为它并没有提供如何实现你想要做的解决方案。
我一直在为这个确切的问题苦苦挣扎了几天,寻找一种方法来保持 web.config 的清洁,并将所有因环境而异的键设置在各自的 transform 文件中。我的结论是,最简单和最稳定的解决方案是在原始的 web.config 中使用调试值,这样当你在 Visual Studio 中进行调试运行时,它们总是存在的。
然后为你想要发布到的不同环境 - 测试、集成、生产 - 创建转换。现在内置的在发布时转换 web.config 文件的功能就足够了。不需要使用 SlowCheetah 或编辑构建事件或项目文件。如果你只有 web 项目的话。
如果你愿意的话,你也可以在你的解决方案中拥有 web.debug.config 文件,只是为了保持一个与开发环境相关的分离文件。确保在其中注释说明这些值在运行 Visual Studio 时不会被应用,以防其他人尝试将其用于此目的!

4
为了使转换在构建时完成:
从项目中排除 web.config (+ web.*.config)。 然后在磁盘上将文件重命名如下:
- web.config => Web.Template.config - web.Release.config => Web.Transform.Release.config - web.Debug.config => Web.Transform.Debug.config
将 web.config 添加到 gitignore,并提交到目前为止所做的更改。
最后,在文本编辑器中将以下内容添加到项目文件中。
  <Target Name="BeforeBuild">
    <TransformXml Source="Web.Template.config" Transform="Web.Transform.$(Configuration).config" Destination="Web.config" />
  </Target>
  <ItemGroup>
    <Content Include="Web.config" />
    <None Include="Web.*.config" />
  </ItemGroup>

现在,每当您构建项目时,Web.Template.config将通过Web.Debug.Transform.config转换为Web.config,如果您在Visual Studio中选择了调试配置,则会进行转换。发布也是如此。

第一次生成Web.config时,您可能需要重新启动Visual Studio并再次构建才能使Visual Studio识别生成的web.config。

此方法已在Visual Studio 2019上的ASP.NET(非Core)项目上进行测试。


听起来很酷。我会试一试。 - HaBo

1
最近,我遇到了与基于.NET Framework 2.0的旧版本web.config文件相同的问题。解决方案很简单,只需删除web.config的命名空间(configuration根节点中的xmlns属性):

之前:<configuration xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0">

之后:<configuration>


0

在你的 appsetting 中添加 "xdt:Transform="Replace" 属性。 像下面这样:

  <appSettings xdt:Transform="Replace">
       <add  key="SMTPPort" value="58" xdt:Transform="Replace" xdt:Locator="Match(key)" />
  </appSettings>

它将用调试配置替换整个appsettings。但是,如果你不想这样做,可以应用任何标签。它将包括该标签的所有子元素。 顺便说一下,你还应该为配置部分设置属性。我在下面标记了:

<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">

0

使用Octopus Deploy(社区版免费),让它为您转换web.config。步骤:

  1. 设置Octopus以部署您的Web应用程序
  2. 确保您的Web.Release.configBuild Action属性设置为Content,就像您的主web.config文件一样。

就这样! Octopus将在没有任何特殊配置的情况下完成其余工作。默认的IIS Web Site部署可以直接使用:enter image description here


数字2是关键 :) - Reza

0

SlowCheeta并不是新产品,它已经存在很长时间了。他们的1.1版本发布于2011年9月8日。 - HaBo
@HaBo 谢谢您的关注,我已将“new”单词从句子中删除。 - Amir Astaneh

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