Web配置转换不起作用


88

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

谁能建议这种配置有什么问题?

Answers:


156

Web.config转换仅在发布操作中应用。

如果您希望在app.config构建操作中完成此操作,则可以使用SlowCheetah-XML Transforms Visual Studio插件:

http://visualstudiogallery.msdn.microsoft.com/69023d00-a4f9-4a34-a6cd-7e854ba318b5


1
非常感谢您,节省了我很多时间。
HaBo 2012年

3
哇,我花了2个小时才找到这个答案。感谢您发布它,我本来可以拔出头发。
花生

似乎在Visual Studio 2015 (Web).config中,转换已成为内置功能,因此您不再需要SlowCheetah。但是内置的转换仅在您发布应用程序时才适用,而不是在您运行它时才适用。您可以在这里查看我是如何解决的。
马特

1
不知道为什么要使用此功能,而@komsky的答案提供了一个简单而干净的解决方案。
卡萨巴·托斯

1
SlowCheetah很棒,但是从他们自己的文档中可以得出:“对于Web项目,在发布或打包应用程序时,文件会发生转换。” 换句话说,不是在调试时。
道格

31

不幸的是,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.configweb.base.config
  • 将一个添加web.config到您的项目。不管里面有什么,因为每次我们进行构建时都会被覆盖,但是我们希望它成为项目的一部分,因此VS.Net不会为我们提供“您的项目未配置用于调试”的弹出窗口,向上。
  • 编辑您的.csproj项目文件,然后将以下TransformXml任务添加到AfterBuild目标。在这里,您可以看到我将使用转换web.base.config文件web.[configuration].config并将其另存为web.config。有关详细信息,请检查这个微软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
我认为对于专业版本,您需要更换Enterprise在路径中用Professional。如果您使用的是预览版本,请另外替换2017Preview

以下是针对不同版本的Visual Studio的路径更改方式的概述(如果您没有Enterprise版本,则可能需要替换EnterpriseProfessional在路径中):

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\

用法: 您可以在构建后事件中使用它(在项目属性中,选择Build Events,然后编辑构建后事件命令行)。命令行参数为(示例):

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

即,首先是配置文件的名称,然后是转换配置文件,然后是可选的模板配置,然后是包含两个文件的项目路径。

我添加了可选的模板config参数,因为否则您的原始完整配置会被转换覆盖,而通过提供模板可以避免这种情况。

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

注意:

  • 如果愿意,还可以将TransformConfig.exe文件复制到上面提到的Visual Studio路径中,并将其复制到Microsoft.Web.XmlTransform.dll需要转换配置的所有项目中。

  • 对于那些想知道为什么我要添加Environment.ExitCode = x;任务的人:从Main返回一个int并不能帮助build事件。在这里查看详细信息

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


1
看起来CodeProject的帖子是虚假的。他将屏幕截图用作代码示例,而自从博客崩溃以来,这些截图已不复存在。
埃里克·劳埃德

3
是的,不幸的是屏幕截图不见了。但是至少仍然有文章文字描述了这种方法。我已在答案中添加了文字说明,以免丢失。
马特2014年

1
没错,也许您可​​以尝试与codeproject的作者James Coleman联系,以在那里进行修复。不过,不确定他是否仍在活动。@ThomasTeilmann
马特

我认为这可能类似于丢失的屏幕截图中的内容。似乎达到了相同的基本结果。 stackoverflow.com/a/6437192/1003916
user1003916

22

回答您的问题并不简单,因为这会带来问题-如果您要使用Web.debug.config转换Web.config,则应将转换效果存储在哪里?在Web.config本身?这将覆盖转换源文件!这可能就是为什么Visual Studio在构建过程中不进行转换的原因。

前面的Matt答案是有效的,但是您可能希望将它们混合使用,以使通用解决方案在您将实际解决方案配置从调试更改为发布等时都可以使用。这是一个简单的解决方案:

  1. 为配置(调试,发布等)创建配置转换
  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>
  1. 导航到项目属性,选择“构建事件”,然后将以下内容添加到“构建后事件”命令行中
@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.为什么XML验证表明TransformXml elemnt在Target元素内无效?(构建工作顺便说一句)。2.现在,这将生成真实的Web.Config,我仍然将Web.Config添加到项目中。现在,每当我在“调试/发布”之间切换时,web.config都会更改,但我不一定要一直将其提交到源存储库中。
Csaba Toth

1.不能真正确定VS如何通过架构验证此XML,但是此警告很常见,因此可以忽略它。2.这取决于您使用的是什么仓库,但是例如您可以使用git.ignore文件条目。
komsky

4
这对我来说效果很好-只需将build-event和proj文件中的12更改为当前版本。对于构建后事件,我使用了:,'"$(MSBuildBinPath)\msbuild.exe" $(ProjectDir)TransformWebConfig.proj /t:TransformWebConfig /p:CurrentConfig=$(ConfigurationName) /p:TargetProjectName=$(TargetPath) 并在.proj文件中更新v12.0v14.0
Jovie

1
对于VS 2017 12.014.0
请将

1
1)不要忘记将生成的web.config包含到Web项目中,否则,发布后将不会复制到目标文件夹。2)如果构建服务器缺少这两个文件,只需将它们复制到服务器“ Microsoft.Web.Publishing.Tasks”,“ Microsoft.Web.XmlTransform”
phiree

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>  

这可能是最好的答案,但是IMO却缺少技巧。如果您Web.config从更改为ContentNone则可以使用Source="Web.config" Destination="$(TargetPath).config"(或可能用于某些类型的项目Destination="$(TargetDir)Web.config")。我也将转换移至AfterBuild,因为在复制文件之前不再需要进行转换。
彼得·泰勒,

好的,实际上这是行不通的,因为出于某种原因我无法将其配置为从运行bin
彼得·泰勒,

4

您的直接问题已得到解答-解释是转换是在发布而不是构建上应用的。

但是,我认为它不能提供有关如何实现您想要做的事情的解决方案。

我已经为这个确切的问题苦苦挣扎了几天,正在寻找一种方法来保持web.config的干净并设置所有密钥,这些密钥根据环境在相应的转换文件中有所不同。我的结论是,最简单,最稳定的解决方案是在原始web.config中使用调试值,这样,当您在Visual Studio中运行调试时,调试值始终存在。

然后为您要发布到的不同环境(测试,集成,生产)创建转换。现在的内置功能足以在发布时转换web.config文件。无需SlowCheetah或编辑构建事件或项目文件。如果只有Web项目。

如果需要,您的解决方案中也可以包含web.debug.config文件,只是为了保留一个单独的文件,其中包含与开发环境有关的所有值。请确保在其中进行注释,以防在其他人尝试将其用于此目的时在Visual Studio中运行时未应用这些值!




0

最近,我对基于.NET Framework 2.0的较旧的web.config文件遇到了相同的问题。解决方案是简单地删除web.config的名称空间(配置根节点中的xmlns attibute ):

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

后: <configuration>

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.