首页 文章

Web Config Transform无法正常工作

提问于
浏览
75

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

任何人都可以建议这种配置可能出错吗?

7 回答

  • 0

    最近我遇到了与基于.NET Framework 2.0的旧web.config相同的问题 . 解决方案只是删除web.config的命名空间(配置根节点中的xmlns attibute):

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

    之后: <configuration>

  • 21

    Web.config转换仅作为发布操作的一部分应用 .

    如果您希望将此作为 app.config 构建操作的一部分来完成,那么您可以使用SlowCheetah - XML Transforms Visual Studio插件:

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

  • 20

    Visual Studio (2010 - 2017) 遗憾的是 not 在调试时直接支持它,它只用于发布 - 即使扩展名为SlowCheetah(标记答案),它也不适用于我(仅适用于使用app.config而不是web.config的项目) .

    Note 有一个 workaround described at codeproject .

    它描述了如何修改.msproj文件以通过转换后的版本覆盖当前的web.config .

    我将首先将该变通方法描述为 Option 1 ,但我最近发现了另一个更容易使用的 Option 2 (因此,如果您愿意,可以直接向下滚动到选项2):


    Option 1: 我已经添加了原始 codeproject article 中的说明(参见上面的链接),因为那里的屏幕截图已经消失了,我不想丢失所有信息:

    在开发和调试本地环境时,VS.Net不会进行任何转换 . 但是,如果您愿意,可以采取一些措施来实现这一目标 .

    • 首先,在 VS.Net 中创建所需的配置,假设默认的调试和发布不足以满足您的要求 .

    • 右键单击 web.config 并选择 Add Config Transforms - 这将为您定义的每个配置创建一个依赖转换配置 .

    • 现在您可以将 web.config 重命名为 web.base.config .

    • web.config 添加到项目中 . 它在什么内容并不重要,因为每次我们进行构建时它都会被覆盖,但我们希望它是项目的一部分,所以 VS.Net 不会给我们"Your Project isn't configured for Debugging"弹出窗口 .

    • 编辑 .csproj Project File 并将以下 TransformXml 任务添加到AfterBuild目标 . 在这里你可以看到我将使用 web.[configuration].config 转换 web.base.config 文件,并将其保存为 web.config . 有关详细信息,请查看this Microsoft Q&A,有关如何扩展构建的说明,请查看there .


    Option 2:

    根据this回答,我开发了一个简单的控制台应用程序,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 .

    编译它并将.exe文件放入目录,例如 C:\MyTools\ .

    Usage: 您可以在 post build event 中使用它(在 project properties 中,选择 Build Events ,然后编辑 Post-build event command line ) . 命令行参数是(示例):

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

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

    我添加了可选的模板配置参数,因为否则您的原始完整配置将被转换覆盖,这可以通过提供模板来避免 .

    只需复制原始Web.config并将其命名为Web.Template.config即可创建模板 .

    Note:

    • 如果您愿意,还可以将 TransformConfig.exe 文件复制到上面提到的_2592403所在的Visual Studio路径中,并在需要转换配置的所有项目中引用它 .

    • 对于那些想知道我为什么添加 Environment.ExitCode = x; 赋值的人:简单地从Main返回一个int对构建事件没有帮助 . 查看详情here.

    • 如果你're publishing your project and you'使用Web.Template.config,确保在发布之前使用正确的配置(通常是Release)对解决方案执行 rebuild . 原因是在调试期间会覆盖Web.Config,否则您最终可能会转换错误的文件 .

  • 139

    回答你的问题并不简单,因为它会带来一个问题 - 如果你想用Web.debug.config转换Web.config - 应该存储转换效果?在Web.config本身?这会覆盖转换源文件!可能这就是为什么Visual Studio在构建期间不进行转换的原因 .

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

    • 为配置创建配置转换(调试,发布等)

    • Web.config 文件重命名为 Web.base.config - 转换应自动重命名( Web.base.Debug.config 等)

    • 将以下 transformWebConfig.proj XML file 添加到项目文件夹:

    <?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>
    
    • 导航到项目属性,选择 Build Events 并将以下内容添加到 Post-build event command line
    @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文件,其中包含有效配置的有效转换 .

  • 4

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

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

    我几天来一直在努力解决这个问题,寻找一种方法来保持web.config干净,并根据相应转换文件中的环境设置所有不同的键 . 我的结论是,最简单和最稳定的解决方案是在原始web.config中使用调试值,这样在Visual Studio中进行调试运行时它们总是存在 .

    然后为您要发布的不同环境创建转换 - 测试,集成, 生产环境 - 无论您拥有什么 . 现在,在发布时转换web.config文件的内置功能就足够了 . 无需SlowCheetah或编辑构建事件或项目文件 . 如果您只有Web项目 .

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

  • 0

    使用Octopus Deploy(社区版是免费的)让它为您转换 web.config . 脚步:

    而已!没有任何特殊配置,八达通将完成其余的工作 . 默认的IIS网站部署将立即执行此操作:
    enter image description here

  • 1

    显然,Visual Studio 2015有一个扩展

    https://visualstudiogallery.msdn.microsoft.com/05bb50e3-c971-4613-9379-acae2cfe6f9e

    通过此包,您可以根据构建配置转换app.config或任何其他XML文件

相关问题