Web Config Transform non funziona


88

In un'applicazione .NET MVC 3.0 ho la seguente configurazione in 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>

Per il debug, ho definito la seguente trasformazione di configurazione:

web.Debug.config

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

E eseguo l'applicazione in modalità di debug, ma la mia porta SMTP sta ancora prendendo il valore da web.config, not web.Debug.config.

Qualcuno può suggerire cosa potrebbe esserci di sbagliato in questa configurazione?

Risposte:


156

Le trasformazioni Web.config vengono applicate solo come parte di un'operazione di pubblicazione.

Se desideri che questa operazione venga eseguita come parte di un'operazione di app.configcompilazione, puoi utilizzare il plug-in SlowCheetah - XML ​​Transforms Visual Studio:

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


1
Grazie mille, mi hai risparmiato molto tempo.
HaBo

3
wow mi ci sono volute 2 ore per trovare questa risposta. Grazie per averlo postato, mi sarei strappato i capelli.
Peanut

Sembra che in Visual Studio 2015 (Web) .config le trasformazioni siano ora una funzionalità incorporata, quindi non è più necessario SlowCheetah. Ma le trasformazioni integrate verranno applicate solo se pubblichi l'applicazione, non se la esegui. Puoi vedere qui come l'ho risolto.
Matt

1
Non sono sicuro del perché usarlo mentre la risposta di @ komsky fornisce una soluzione semplice e pulita.
Csaba Toth

1
SlowCheetah è fantastico, ma dalla loro stessa documentazione: "Per i progetti web i file vengono trasformati quando pubblichi o impacchetterai la tua applicazione". In altre parole, non durante il debug.
Doug

31

Visual Studio (2010-2019) purtroppo non lo supporta direttamente durante il debug, è inteso solo per la pubblicazione - anche con l'estensione SlowCheetah (risposta contrassegnata) non funziona per me (solo per progetti che utilizzano app.config anziché web.config).

Notare che esiste una soluzione alternativa descritta in codeproject .

Descrive come modificare il file .msproj per sovrascrivere il web.config corrente con la versione trasformata.

Descriverò prima quella soluzione alternativa come Opzione 1 , ma di recente ho scoperto un'altra opzione 2 , che è più facile da usare (quindi puoi scorrere direttamente fino all'opzione 2 se lo desideri):


Opzione 1: ho aggiunto le istruzioni tratte dall'articolo originale del progetto di codice (vedi il link sopra), perché le schermate sono già sparite e non voglio perdere l'intera informazione:

VS.Net non esegue alcuna trasformazione durante lo sviluppo e il debug dell'ambiente locale. Ma ci sono alcuni passaggi che puoi fare per farlo accadere, se lo desideri.

  • Innanzitutto, crea le configurazioni che desideri in VS.Net , assumendo che il debug e il rilascio predefiniti non siano sufficienti per ciò che stai cercando di ottenere.
  • Fare clic con il pulsante destro del mouse web.confige selezionare Aggiungi trasformazioni di configurazione : questo creerà una configurazione di trasformazione dipendente per ciascuna delle configurazioni definite.
  • Ora puoi rinominare il tuo web.configin web.base.config.
  • Aggiungi una web.configal tuo progetto. Non importa cosa c'è dentro perché verrà sovrascritto ogni volta che facciamo una compilazione, ma vogliamo che faccia parte del progetto in modo che VS.Net non ci dia il pop "Il tuo progetto non è configurato per il debug "- su.
  • Modifica il tuo .csprojfile di progetto e aggiungi la seguente TransformXmlattività alla destinazione AfterBuild. Qui puoi vedere che trasformerò il web.base.configfile usando il web.[configuration].confige lo salverò come web.config. Per i dettagli, consultare queste domande e risposte di Microsoft e per istruzioni su come estendere la build, vedere .

Opzione 2:

Sulla base di questa risposta, ho sviluppato una semplice app per console, TransformConfig.exe (con sintassi 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);
    }

  }
}

Assicurarsi di aggiungere la DLL "C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\v14.0\Web\Microsoft.Web.XmlTransform.dll"come riferimento (questo esempio si applica a VS 2015, per le versioni precedenti sostituire il v14.0nel percorso con il numero di versione appropriato, ad esempio v11.0).

Per Visual Studio 2017, sullo schema di denominazione per il percorso è cambiato: per esempio, per la versione enterprise è qui: C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\MSBuild\Microsoft\VisualStudio\v15.0\Web.
Presumo che per la versione professionale sia necessario sostituire Enterprisenel percorso da Professional. Se stai utilizzando la versione di anteprima, sostituisci inoltre 2017con Preview.

Ecco una panoramica di come il percorso è cambiato per le diverse versioni di Visual Studio (se non si dispone della versione Enterprise può essere necessario sostituire Enterpriseda Professionalnel percorso):

VS Version         Path (for 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

Compilalo e metti il ​​file .exe in una directory, ad es C:\MyTools\.

Utilizzo: puoi usarlo nel tuo evento post build (nelle proprietà del progetto , seleziona Build Events , quindi modifica la riga di comando dell'evento post build ). I parametri della riga di comando sono (esempio):

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

cioè prima il nome del file di configurazione, seguito dal file di configurazione della trasformazione, seguito da un modello di configurazione opzionale, seguito dal percorso del progetto contenente entrambi i file.

Ho aggiunto il parametro di configurazione del modello opzionale perché altrimenti la configurazione completa originale verrebbe sovrascritta dalla trasformazione, cosa che può essere evitata fornendo un modello.

Crea il modello semplicemente copiando il Web.config originale e chiamalo Web.Template.config.

Nota:

  • Se preferisci, puoi anche copiare il TransformConfig.exefile nel percorso di Visual Studio sopra menzionato dove Microsoft.Web.XmlTransform.dllrisiede e fare riferimento ad esso in tutti i tuoi progetti in cui devi trasformare le tue configurazioni.

  • Per quelli di voi che si stanno chiedendo perché ho aggiunto gli Environment.ExitCode = x;incarichi: semplicemente restituire un int da Main non ha aiutato nell'evento build. Vedi i dettagli qui.

  • Se stai pubblicando il tuo progetto e stai usando un Web.Template.config, assicurati di aver fatto una ricostruzione sulla tua soluzione con la giusta configurazione (di solito Release) prima di pubblicare. Il motivo è che Web.Config viene sovrascritto durante il debug e potresti finire per trasformare il file sbagliato altrimenti.


1
Sembra che il post di CodeProject sia bloccato. Ha usato screenshot per i suoi esempi di codice e ora da quando il suo blog non è attivo, sono persi nella storia.
Eric Lloyd

3
Sì, purtroppo gli screenshot sono spariti. Ma almeno il testo dell'articolo è ancora lì, che descrive l'approccio. Ho aggiunto la descrizione del testo alla mia risposta per evitare di perderla.
Matt

1
È vero, forse si può provare a contattare l'autore James Coleman a codeproject per risolverlo lì. Non sono sicuro che sia ancora attivo lì, però. @ThomasTeilmann
Matt

Penso che questo potrebbe essere simile a quello che c'era negli screenshot persi. Sembra ottenere lo stesso risultato di base. stackoverflow.com/a/6437192/1003916
user1003916

22

Rispondere alla tua domanda non è semplice, perché pone un problema - se vuoi trasformare Web.config con Web.debug.config - dove dovrebbe essere memorizzato l'effetto di trasformazione? Nello stesso Web.config? Questo sovrascriverebbe il file sorgente della trasformazione! Probabilmente è per questo che Visual Studio non esegue trasformazioni durante le compilazioni.

La precedente risposta di Matt è valida, ma potresti volerli mescolare per avere una soluzione generica che funzioni quando effettivamente cambi la configurazione della soluzione attiva dal debug al rilascio ecc. Ecco una soluzione semplice:

  1. Crea le tue trasformazioni di configurazione per le configurazioni (debug, rilascio, ecc.)
  2. Rinomina Web.configfile in Web.base.config: le trasformazioni devono essere rinominate automaticamente di conseguenza ( Web.base.Debug.config, ecc.)
  3. Aggiungi il seguente file XML transformWebConfig.proj alla cartella del progetto:
<?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. Passa alle proprietà del progetto, scegli Eventi di compilazione e aggiungi il seguente contenuto alla riga di comando dell'evento post-compilazione :
@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)

Ora, quando si crea la soluzione, verrà creato un file Web.config con trasformazioni valide per la configurazione attiva.


Risposta più pulita e migliore. Alcune domande: 1. Perché le convalide XML dicono che l'elemento TransformXml non è valido all'interno dell'elemento Target? (la build funziona BTW). 2. Ora che questo genera il vero Web.Config, aggiungo ancora Web.Config al progetto. Ora ogni volta che passo da Debug a Release, web.config cambierà, ma non voglio necessariamente eseguire il commit di tutto il tempo nel repository di origine.
Csaba Toth

1. Non si può davvero dire in che modo VS convalida questo XML con lo schema, ma questo avviso è comune, quindi è possibile ignorarlo. 2. Dipende dal repository che stai utilizzando, ma puoi ad esempio utilizzare la voce del file git.ignore.
Komsky

4
Questo ha funzionato bene per me: ho appena cambiato i 12 nel file build-event e proj nella versione corrente. Per l'evento di post-compilazione ho usato: '"$(MSBuildBinPath)\msbuild.exe" $(ProjectDir)TransformWebConfig.proj /t:TransformWebConfig /p:CurrentConfig=$(ConfigurationName) /p:TargetProjectName=$(TargetPath) e aggiornato v12.0a v14.0nel file .proj.
Jovie

1
Per VS 2017 modifica ogni 12.0a14.0
Csaba Toth

1
1) non dimenticare di includere il web.config generato nel progetto web, altrimenti non verrà copiato nella cartella di destinazione dopo la pubblicazione. 2) se nel server di compilazione mancano questi due file, è sufficiente copiarli sul server "Microsoft.Web.Publishing.Tasks", "Microsoft.Web.XmlTransform"
phiree

8

per VS 2017 ho trovato la risposta qui non sono sicuro del motivo per cui nessuno l'ha fatto riferimento sopra in quanto sembra essere una soluzione molto popolare. Anche molto facile. Assicurati di vedere il commento di IOrlandoni su 5 marzo 2019 per farlo funzionare in VS 2017 e tutte le versioni.

Fondamentalmente è un due stepper. Innanzitutto, modifica il file .csproj, aggiungendo il codice seguente. In secondo luogo, crei una nuova configurazione web.base.config e copi lì il web.config esistente. Dopo averlo fatto, qualsiasi build sovrascriverà il tuo web.config con la trasformazione desiderata.

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

Questa è probabilmente la risposta migliore, ma IMO manca un trucco. Se passi Web.configda Contenta Noneallora puoi usare Source="Web.config" Destination="$(TargetPath).config"(o forse per alcuni tipi di progetto, Destination="$(TargetDir)Web.config"). Ho anche spostato la trasformazione in AfterBuild, poiché non è più necessario eseguirla prima di copiare i file.
Peter Taylor

Ok, in realtà non funziona perché per qualche motivo non riesco a configurarlo per l'esecuzione bin.
Peter Taylor

4

La tua domanda immediata ha ricevuto risposta: la spiegazione è che la trasformazione viene applicata alla pubblicazione, non alla compilazione.

Tuttavia, penso che non offra una soluzione su come ottenere ciò che si desidera fare.

Ho lottato con questo problema esatto per alcuni giorni, cercando un modo per mantenere pulito web.config e impostare tutte le chiavi che variano a seconda dell'ambiente nei rispettivi file di trasformazione. La mia conclusione è che la soluzione più semplice e stabile è usare i valori di debug nel web.config originale, in questo modo sono sempre presenti quando esegui il debug in Visual Studio.

Quindi crea trasformazioni per i diversi ambienti in cui desideri pubblicare: test, integrazione, produzione, qualunque cosa tu abbia. La funzionalità ora incorporata per trasformare i file web.config durante la pubblicazione sarà sufficiente per questo. Non è necessario SlowCheetah o modificare eventi di build né file di progetto. Se hai solo progetti web che è.

Se lo desideri, potresti anche avere il file web.debug.config nella tua soluzione, solo per mantenere un file separato con tutti i valori relativi all'ambiente di sviluppo. Assicurati di commentare che i valori non vengono applicati durante l'esecuzione in Visual Studio, nel caso in cui qualcun altro provi a usarlo per quello scopo!


1

Usa Octopus Deploy (l'edizione Community è gratuita) e lascia che trasformi il web.configper te. Passaggi:

  1. Configura Octopus per distribuire la tua applicazione web
  2. Assicurati che Web.Release.configla Build Actionproprietà sia impostata su Contentcome il tuo web.configfile principale .

Questo è tutto! Octopus farà il resto senza alcuna configurazione speciale. Una distribuzione del sito Web IIS predefinita lo farà immediatamente:inserisci qui la descrizione dell'immagine


Il numero 2 è la chiave :)
Reza


0

Recentemente ho avuto lo stesso problema con un vecchio file web.config basato su .NET Framework 2.0. La soluzione era semplicemente rimuovere lo spazio dei nomi di web.config ( attributo xmlns nel nodo radice di configurazione ):

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

DOPO: <configuration>

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.