Web Config Transform не работает

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.

Кто-нибудь может подсказать, что может быть не так в этой конфигурации?

ХаБо
источник

Ответы:

156

Преобразования Web.config применяются только как часть операции публикации.

Если вы хотите, чтобы это выполнялось как часть app.configоперации сборки, вы можете использовать плагин SlowCheetah - XML ​​Transforms Visual Studio:

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

devdigital
источник
1
Большое вам спасибо, что вы сэкономили мне много времени.
HaBo
3
Вау, мне потребовалось 2 часа, чтобы найти этот ответ. Спасибо, что разместили, я бы выдергивал волосы.
Peanut
Похоже, что в Visual Studio 2015 (Web) преобразования .config теперь являются встроенной функцией, поэтому SlowCheetah больше не нужен. Но встроенные преобразования будут применяться только при публикации приложения, а не при его запуске. Вы можете посмотреть здесь, как я это решил.
Мэтт
1
Не уверен, зачем это использовать, в то время как ответ @komsky предлагает простое и понятное решение.
Csaba Toth
1
SlowCheetah великолепен, но из их собственной документации: «Для веб-проектов файлы преобразуются, когда вы публикуете или упаковываете свое приложение». Другими словами, не во время отладки.
Дуг
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.configв web.base.config.
  • Добавьте web.configв свой проект. Это не имеет значения , что в нем , потому что он будет перезаписан каждый раз , когда мы делаем сборку , но мы хотим , чтобы это часть проекта так VS.Net не дают нам «Ваш проект не настроен для отладки» поп- вверх.
  • Отредактируйте файл .csprojпроекта и добавьте следующую TransformXmlзадачу в цель AfterBuild. Здесь вы можете видеть, что я буду преобразовывать web.base.configфайл с помощью, web.[configuration].configи он сохранит его как web.config. Для получения подробной информации, пожалуйста, проверьте эти вопросы и ответы Microsoft, а инструкции по расширению сборки смотрите там .

Вариант 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. Если вы используете предварительную версию, дополнительно замените 2017на Preview.

Вот обзор того, как путь изменился для разных версий Visual Studio (если у вас нет версии Enterprise, вам может потребоваться заменить Enterpriseна Professionalв пути):

        Путь к Microsoft.Web.XmlTransform.dllверсии VS (для )
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 , затем отредактируйте командную строку Post-build event ). Параметры командной строки (пример):

"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;назначения: простой возврат int из Main не помог в событии сборки. Подробности здесь.

  • Если вы публикуете свой проект и используете Web.Template.config, перед публикацией убедитесь, что вы выполнили перестройку своего решения с правильной конфигурацией (обычно Release). Причина в том, что Web.Config перезаписывается во время отладки, и в противном случае вы можете преобразовать не тот файл.

Мэтт
источник
1
Похоже, сообщение CodeProject собрано. Он использовал скриншоты для своих примеров кода, и теперь, когда его блог не работает, они потеряны для истории.
Эрик Ллойд
3
Да, к сожалению, скриншотов нет. Но, по крайней мере, там есть текст статьи, описывающий подход. Я добавил текстовое описание к своему ответу, чтобы не потерять его.
Мэтт
1
Правда, может быть, можно попробовать связаться с автором Джеймсом Коулманом в codeproject, чтобы исправить это там. Не уверен, что он все еще там активен. @ThomasTeilmann
Мэтт
Я думаю, это может быть похоже на то, что было на потерянных скриншотах. Кажется, достигается тот же базовый результат. stackoverflow.com/a/6437192/1003916
user1003916
22

Ответить на ваш вопрос непросто, потому что это создает проблему - если вы хотите преобразовать Web.config с помощью Web.debug.config - где следует сохранить эффект преобразования? В самом Web.config? Это приведет к перезаписи исходного файла преобразования! Вероятно, поэтому Visual Studio не выполняет преобразований во время сборки.

Предыдущий ответ Мэтта действителен, но вы можете смешать их, чтобы получить общее решение, которое работает, когда вы фактически меняете конфигурацию активного решения с отладки до выпуска и т. Д. Вот простое решение:

  1. Создайте свои преобразования конфигурации для конфигураций (отладка, выпуск и т. Д.)
  2. Переименовать Web.configфайл в Web.base.config- преобразования должны автоматически переименовывать соответственно ( Web.base.Debug.configи т. Д.)
  3. Добавьте следующий XML-файл transformWebConfig.proj в папку проекта:
<?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 недопустим в элементе Target? (сборка работает BTW). 2. Теперь, когда создается настоящий Web.Config, я все еще добавляю Web.Config в проект. Теперь каждый раз, когда я переключаюсь между Debug / Release, web.config будет меняться, но я не обязательно хочу фиксировать это все время в исходном репозитории.
Csaba Toth
1. Не могу точно сказать, как VS проверяет этот XML с помощью схемы, но это предупреждение является обычным, поэтому вы можете его игнорировать. 2. Это зависит от того, какое репо вы используете, но вы можете, например, использовать запись в файле git.ignore.
Комский
4
Это сработало для меня - просто изменил 12 в файле build-event и proj на текущую версию. Для события после сборки я использовал: '"$(MSBuildBinPath)\msbuild.exe" $(ProjectDir)TransformWebConfig.proj /t:TransformWebConfig /p:CurrentConfig=$(ConfigurationName) /p:TargetProjectName=$(TargetPath) и обновил его v12.0до v14.0файла .proj.
Джови
1
Для VS 2017 изменить каждый 12.0к14.0
Чаба Toth
1
1) не забудьте включить сгенерированный файл web.config в веб-проект, иначе он не будет копироваться в целевую папку после публикации. 2) если на сервере сборки отсутствуют эти два файла, просто скопируйте их на сервер «Microsoft.Web.Publishing.Tasks», «Microsoft.Web.XmlTransform»
phiree
8

для VS 2017 я нашел здесь ответ, не зная, почему никто не упомянул об этом выше, поскольку это кажется очень популярным решением. Тоже очень просто. Убедитесь, что вы видите комментарий IOrlandoni от 5 марта 2019 г., чтобы он работал в 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.configс Contentна, Noneто вы можете использовать Source="Web.config" Destination="$(TargetPath).config"(или, возможно, для некоторых типов проектов Destination="$(TargetDir)Web.config"). Я также переместил преобразование в AfterBuild, так как его больше не нужно выполнять перед копированием файлов.
Питер Тейлор
Хорошо, на самом деле это не работает, потому что по какой-то причине я не могу настроить его для запуска bin.
Питер Тейлор
4

На ваш непосредственный вопрос был дан ответ - объяснение состоит в том, что преобразование применяется при публикации, а не при сборке.

Однако я думаю, что он не предлагает решения о том, как достичь того, что вы хотите делать.

Я боролся с этой конкретной проблемой уже несколько дней, ища способ сохранить чистоту web.config и установить все ключи, которые меняются в зависимости от среды в соответствующих файлах преобразования. Я пришел к выводу, что самым простым и стабильным решением является использование значений отладки в исходном файле web.config, таким образом, они всегда присутствуют при выполнении отладки в Visual Studio.

Затем создайте преобразования для различных сред, в которых вы хотите публиковать - тестирование, интеграцию, производство - все, что у вас есть. Для этого достаточно встроенной функции преобразования файлов web.config при публикации. Нет необходимости в SlowCheetah или редактировании событий сборки или файлов проекта. Если у вас есть только веб-проекты, то есть.

При желании вы также можете иметь в своем решении файл web.debug.config, просто чтобы сохранить отдельный файл со всеми значениями, относящимися к среде разработки. Обязательно укажите в нем, что значения не применяются при работе в Visual Studio, на случай, если кто-то другой попытается использовать его для этой цели!

Каролина Перссон
источник
1

Используйте Octopus Deploy (бесплатная версия для сообщества), и пусть она изменит web.configвашу платформу . Шаги:

  1. Настройте Octopus для развертывания вашего веб-приложения
  2. Убедитесь, что у вас Web.Release.configустановлено Build Actionсвойство, Contentтакое же, как у вашего основного web.configфайла.

Это оно! Все остальное Octopus сделает без специальной настройки. Развертывание веб-сайта IIS по умолчанию сделает это из коробки:введите описание изображения здесь

Мэтт Кодж
источник
Номер 2 - это ключ :)
Реза
0

Видимо есть расширение для Visual Studio 2015

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

Этот пакет позволяет преобразовать файл app.config или любой другой XML-файл на основе конфигурации сборки.

Амир Астанех
источник
SlowCheeta - не новый. он там был давно. Их выпуск 1.1 был 9/8/2011
HaBo
@HaBo Спасибо за внимание, я удалил newмир как предложение.
Амир Астане
0

Недавно у меня была такая же проблема со старым файлом web.config, основанным на .NET Framework 2.0. Решением было просто удалить пространство имен web.config ( атрибут xmlns в корневом узле конфигурации ):

ПЕРЕД: <configuration xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0">

ПОСЛЕ: <configuration>

Рафаэль Нето
источник