O Web Config Transform não funciona


88

Em um aplicativo .NET MVC 3.0, tenho a seguinte configuração em 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>

Para depuração, tenho a seguinte transformação de configuração definida:

web.Debug.config

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

E eu executo o aplicativo no modo de depuração, mas minha porta SMTP ainda está recebendo o valor de web.config, não web.Debug.config.

Alguém pode sugerir o que pode estar errado nesta configuração?

Respostas:


155

As transformações Web.config são aplicadas apenas como parte de uma operação de publicação.

Se desejar que isso seja feito como parte de uma app.configoperação de construção, você pode usar o plugin SlowCheetah - XML ​​Transforms Visual Studio:

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


1
Muito obrigado, você me economizou muito tempo.
HaBo

3
wow me levou 2 horas para encontrar esta resposta. Obrigado por postar, eu estaria arrancando meus cabelos.
Amendoim

Parece que no Visual Studio 2015 (Web) .config transformações agora são um recurso interno, então você não precisa mais do SlowCheetah. Mas as transformações integradas só se aplicam se você publicar o aplicativo, não se executá-lo. Você pode ver aqui como eu resolvi isso.
Matt

1
Não sei por que usar isso, enquanto a resposta de @komsky fornece uma solução simples e limpa.
Csaba Toth

1
SlowCheetah é ótimo, mas a partir de sua própria documentação: "Para projetos da web, os arquivos são transformados quando você publica ou empacota seu aplicativo." Em outras palavras, não durante a depuração.
Doug

31

O Visual Studio (2010 - 2019) infelizmente não oferece suporte direto durante a depuração, ele se destina apenas à publicação - mesmo com a extensão SlowCheetah (resposta marcada), ele não funciona para mim (apenas para projetos que usam app.config em vez de web.config).

Observe que há uma solução alternativa descrita em codeproject .

Ele descreve como modificar o arquivo .msproj para substituir o web.config atual pela versão transformada.

Descreverei primeiro essa solução alternativa como Opção 1 , mas recentemente descobri outra Opção 2 , que é mais fácil de usar (portanto, você pode rolar para baixo até a opção 2 diretamente, se desejar):


Opção 1: adicionei as instruções retiradas do artigo original do codeproject (veja o link acima), porque as capturas de tela lá já sumiram e não quero perder todas as informações:

VS.Net não faz nenhuma transformação quando você está desenvolvendo e apenas depurando seu ambiente local. Mas existem algumas etapas que você pode seguir para que isso aconteça, se desejar.

  • Primeiro, crie as configurações desejadas no VS.Net , assumindo que a depuração e a liberação padrão não são suficientes para o que você está tentando realizar.
  • Clique com o botão direito em seu web.confige selecione Adicionar Transformações de Configuração - isso criará uma configuração de transformação dependente para cada uma das configurações definidas.
  • Agora você pode renomear seu web.configpara web.base.config.
  • Adicione um web.configao seu projeto. Não importa o que está nele, porque ele será sobrescrito toda vez que fizermos uma compilação, mas queremos que faça parte do projeto para que o VS.Net não nos dê o pop-up "Seu projeto não está configurado para depuração" acima.
  • Edite seu .csprojarquivo de projeto e adicione a seguinte TransformXmltarefa ao destino AfterBuild. Aqui você pode ver que irei transformar o web.base.configarquivo usando o web.[configuration].confige salvarei como web.config. Para obter detalhes, verifique este Q&A da Microsoft e, para obter instruções sobre como estender a compilação, consulte aqui .

Opção 2:

Com base nessa resposta, desenvolvi um aplicativo de console simples, TransformConfig.exe (na sintaxe 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);
    }

  }
}

Certifique-se de adicionar a DLL "C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\v14.0\Web\Microsoft.Web.XmlTransform.dll"como uma referência (este exemplo se aplica ao VS 2015, para versões mais antigas, substitua v14.0no caminho pelo número de versão apropriado, por exemplo v11.0).

Para Visual Studio 2017, o esquema de nomeação para o caminho foi alterado: Por exemplo, para a versão Enterprise é aqui: C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\MSBuild\Microsoft\VisualStudio\v15.0\Web.
Presumo que, para a versão profissional, você precise substituir Enterpriseno caminho por Professional. Se você estiver usando a versão de visualização, substitua adicionalmente 2017por Preview.

Esta é uma visão geral de como o caminho mudou para diferentes versões do Visual Studio (se você não tiver a versão Enterprise, pode ser necessário substituir Enterprisepor Professionalno caminho):

        Caminho da Microsoft.Web.XmlTransform.dllversão do VS (para )
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

Compile-o e coloque o arquivo .exe em um diretório, por exemplo C:\MyTools\.

Uso: você pode usá-lo em seu evento de pós-construção (nas propriedades do projeto , selecione Eventos de construção e edite a linha de comando do evento de pós-construção ). Os parâmetros da linha de comando são (exemplo):

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

ou seja, primeiro o nome do arquivo de configuração, seguido pelo arquivo de configuração de transformação, seguido por uma configuração de modelo opcional, seguido pelo caminho para seu projeto contendo ambos os arquivos.

Eu adicionei o parâmetro opcional de configuração do modelo porque, caso contrário, sua configuração completa original seria sobrescrita pela transformação, o que pode ser evitado fornecendo um modelo.

Crie o modelo simplesmente copiando o Web.config original e nomeie-o Web.Template.config.

Nota:

  • Se preferir, você também pode copiar o TransformConfig.exearquivo para o caminho do Visual Studio mencionado acima, onde Microsoft.Web.XmlTransform.dllreside o, e consultá-lo em todos os seus projetos nos quais você precisa transformar suas configurações.

  • Para aqueles que estão se perguntando por que adicionei Environment.ExitCode = x;atribuições: O simples retorno de um int de Main não ajudou no evento de construção. Veja os detalhes aqui.

  • Se você estiver publicando seu projeto e estiver usando um Web.Template.config, certifique-se de reconstruir sua solução com a configuração correta (geralmente Release) antes de publicar. A razão é que o Web.Config é sobrescrito durante a depuração e você pode acabar transformando o arquivo errado de outra forma.


1
Parece que a postagem do CodeProject foi enviada. Ele usou capturas de tela para seus exemplos de código e agora, desde que seu blog caiu, eles se perderam na história.
Eric Lloyd

3
Sim, infelizmente as imagens sumiram. Mas pelo menos o texto do artigo ainda está lá, descrevendo a abordagem. Eu adicionei o texto descritivo à minha resposta para evitar perdê-la.
Matt

1
Verdade, talvez alguém possa tentar entrar em contato com o autor James Coleman em codeproject para consertar lá. Não tenho certeza se ele ainda está ativo lá, no entanto. @ThomasTeilmann
Matt

Eu acho que isso pode ser semelhante ao que estava nas imagens perdidas. Parece atingir o mesmo resultado básico. stackoverflow.com/a/6437192/1003916
user1003916

22

Responder à sua pergunta não é simples, porque apresenta um problema - se você deseja transformar Web.config com Web.debug.config - onde o efeito de transformação deve ser armazenado? No próprio Web.config? Isso sobrescreveria o arquivo de origem da transformação! Provavelmente é por isso que o Visual Studio não faz transformações durante as compilações.

A resposta anterior de Matt é válida, mas você pode querer misturá-los para ter uma solução genérica que funcione quando você realmente altera a configuração da solução ativa de depuração para liberação, etc. Aqui está uma solução simples:

  1. Crie suas transformações de configuração para configurações (depuração, liberação, etc)
  2. Renomear Web.configarquivo para Web.base.config- as transformações devem renomear automaticamente de acordo ( Web.base.Debug.config, etc)
  3. Adicione o seguinte arquivo transformWebConfig.proj XML à pasta do projeto:
<?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. Navegue até as propriedades do projeto, escolha Build Events e adicione o seguinte conteúdo à linha de comando do evento Post-build :
@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)

Agora, quando você construir sua solução, um arquivo Web.config será criado com transformações válidas para configuração ativa.


A resposta mais limpa e melhor. Algumas perguntas: 1. Por que as validações XML dizem que o elemento TransformXml é inválido no elemento Target? (a construção funciona BTW). 2. Agora que isso gera o Web.Config real, ainda adiciono o Web.Config ao projeto. Agora, sempre que eu alternar entre Debug / Release, o web.config mudará, mas não quero necessariamente comprometer isso o tempo todo no repositório de origem.
Csaba Toth

1. Não é possível dizer como o VS valida este XML com esquema, mas este aviso é comum, então você pode ignorá-lo. 2. Depende de qual repo você está usando, mas você pode, por exemplo, usar a entrada do arquivo git.ignore.
komsky

4
Isso funcionou bem para mim - apenas alterei os 12 no arquivo de evento de construção e proj para a versão atual. Para o evento pós-compilação que eu usei: '"$(MSBuildBinPath)\msbuild.exe" $(ProjectDir)TransformWebConfig.proj /t:TransformWebConfig /p:CurrentConfig=$(ConfigurationName) /p:TargetProjectName=$(TargetPath) e atualizado v12.0para v14.0no arquivo proj.
Jovie

1
Para VS 2017, modifique a cada 12.0para14.0
Csaba Toth

1
1) não se esqueça de incluir o web.config gerado no projeto da web, ou ele não será copiado para a pasta de destino depois de publicado. 2) se o servidor de compilação não tiver esses dois arquivos, basta copiá-los para o servidor "Microsoft.Web.Publishing.Tasks", "Microsoft.Web.XmlTransform"
phiree

7

para o VS 2017, encontrei a resposta aqui, não tenho certeza por que ninguém fez referência a ela acima, pois parece ser uma solução muito popular. Muito fácil também. Certifique-se de ver o comentário de IOrlandoni em 5 de março de 2019 para fazer funcionar no VS 2017 e todas as versões.

Basicamente, é um dois passos. Primeiro, você edita o arquivo .csproj, anexando o código abaixo. Em segundo lugar, você cria uma nova configuração web.base.config e copia o web.config existente lá. Depois de fazer isso, qualquer construção sobrescreverá seu web.config com a transformação desejada.

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

Esta é provavelmente a melhor resposta, mas IMO está faltando um truque. Se você mudar Web.configde Contentpara None, poderá usar Source="Web.config" Destination="$(TargetPath).config"(ou talvez para alguns tipos de projeto Destination="$(TargetDir)Web.config"). Também movi a transformação para AfterBuild, uma vez que não é mais necessário fazer antes que os arquivos sejam copiados.
Peter Taylor de

Ok, na verdade isso não funciona porque por algum motivo não consigo configurá-lo para ser executado bin.
Peter Taylor de

4

Sua pergunta imediata foi respondida - a explicação é que a transformação é aplicada na publicação, não na construção.

No entanto, acho que não oferece uma solução sobre como conseguir o que você deseja fazer.

Eu tenho lutado com esse problema exato por alguns dias, procurando uma maneira de manter o web.config limpo e definir todas as chaves que variam dependendo do ambiente nos respectivos arquivos de transformação. Minha conclusão é que a solução mais fácil e estável é usar valores de depuração no web.config original, dessa forma, eles estão sempre presentes quando você faz execuções de depuração no Visual Studio.

Em seguida, crie transformações para os diferentes ambientes para os quais deseja publicar - teste, integração, produção - o que quer que você tenha. A funcionalidade agora integrada para transformar arquivos web.config na publicação será suficiente para isso. Não há necessidade de SlowCheetah ou edição de eventos de construção ou arquivos de projeto. Se você tiver apenas projetos da web, é isso.

Se desejar, você também pode ter o arquivo web.debug.config em sua solução, apenas para manter um arquivo separado com todos os valores pertencentes ao ambiente de desenvolvimento. Não deixe de comentar que os valores não são aplicados durante a execução no Visual Studio, no caso de alguém tentar usá-lo para esse propósito!


1

Use o Octopus Deploy (a edição Community é gratuita) e deixe-o transformar o web.configpara você. Passos:

  1. Configure o Octopus para implantar seu aplicativo da web
  2. Certifique-se de que Web.Release.configtem a Build Actionpropriedade definida Contentcomo igual ao seu web.configarquivo principal .

É isso aí! O Octopus fará o resto sem nenhuma configuração especial. Uma implantação padrão do site do IIS fará isso imediatamente:insira a descrição da imagem aqui


O número 2 é a chave :)
Reza


0

Recentemente, tive o mesmo problema com um arquivo web.config antigo baseado no .NET Framework 2.0. A solução foi simplesmente remover o namespace do web.config ( xmlns attibute no nó raiz de configuração ):

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

DEPOIS DE: <configuration>

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.