Web Config Transform funktioniert nicht

88

In einer .NET MVC 3.0-Anwendung habe ich die folgende Konfiguration in appSettings:

web.config

<appSettings>
<add key="SMTPHost" value="mail.domain.com"/>
    <add key="SMTPUsername" value="[email protected]"/>
    <add key="SMTPPort" value="25"/>
    <add key="SMTPPwd" value="mypassword"/>
    <add key="EmailFrom" value="[email protected]"/>
</appSettings>

Für das Debuggen habe ich folgende Konfigurationstransformation definiert:

web.Debug.config

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

Und ich führe die Anwendung im Debug-Modus aus, aber mein SMTP-Port übernimmt immer noch den Wert von web.config, nicht web.Debug.config.

Kann jemand vorschlagen, was in dieser Konfiguration falsch sein könnte?

HaBo
quelle

Antworten:

155

Die Web.config-Transformationen werden nur als Teil eines Veröffentlichungsvorgangs angewendet.

Wenn Sie möchten, dass dies als Teil einer app.configErstellungsoperation ausgeführt wird, können Sie das Plugin SlowCheetah - XML ​​Transforms Visual Studio verwenden:

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

devdigital
quelle
1
Vielen Dank, dass Sie mir viel Zeit gespart haben.
HaBo
3
Wow, ich habe 2 Stunden gebraucht, um diese Antwort zu finden. Danke, dass du es gepostet hast. Ich hätte mir die Haare ausgezogen.
Erdnuss
Es scheint, dass in Visual Studio 2015 (Web) .config-Transformationen jetzt eine integrierte Funktion sind, sodass Sie SlowCheetah nicht mehr benötigen. Die integrierten Transformationen gelten jedoch nur, wenn Sie die Anwendung veröffentlichen, nicht, wenn Sie sie ausführen. Sie können hier sehen , wie ich es gelöst habe.
Matt
1
Ich bin mir nicht sicher, warum ich das verwenden soll, während die Antwort von @ komsky eine einfache und saubere Lösung bietet.
Csaba Toth
1
SlowCheetah ist großartig, aber aus der eigenen Dokumentation: "Bei Webprojekten werden die Dateien transformiert, wenn Sie Ihre Anwendung veröffentlichen oder verpacken." Mit anderen Worten, nicht beim Debuggen.
Doug
31

Visual Studio (2010 - 2019) unterstützt es leider nicht direkt während des Debuggens, es ist nur zum Veröffentlichen gedacht - selbst mit der Erweiterung SlowCheetah (markierte Antwort) funktioniert es nicht für mich (nur für Projekte, die app.config verwenden und nicht web.config).

Beachten Sie, dass es eine Abhilfe bei Codeproject beschrieben .

Es wird beschrieben, wie Sie die .msproj-Datei so ändern, dass die aktuelle web.config durch die transformierte Version überschrieben wird.

Ich werde diese Problemumgehung zunächst als Option 1 beschreiben , habe jedoch kürzlich eine andere Option 2 herausgefunden , die einfacher zu verwenden ist (Sie können also direkt zu Option 2 scrollen, wenn Sie möchten):


Option 1: Ich habe die Anweisungen aus dem ursprünglichen Artikel zum Codeprojekt hinzugefügt (siehe Link oben), da die dortigen Screenshots bereits verschwunden sind und ich nicht die gesamten Informationen verlieren möchte:

VS.Net führt keine Transformation durch, wenn Sie Ihre lokale Umgebung entwickeln und nur debuggen. Es gibt jedoch einige Schritte, die Sie ausführen können, um dies zu erreichen, wenn Sie möchten.

  • Erstellen Sie zunächst die gewünschten Konfigurationen in VS.Net , vorausgesetzt, das Standard-Debugging und die Standardversion reichen nicht für das aus, was Sie erreichen möchten .
  • Rechts auf klicken web.configund wählen Sie Config Wandelt hinzufügen - das eine abhängige Transformation Config für jede Ihrer Konfigurationen definiert schaffen.
  • Jetzt können Sie Ihre web.configin umbenennen web.base.config.
  • Fügen Sie web.configIhrem Projekt ein hinzu. Es spielt keine Rolle , was darin ist , weil es uns jedes Mal einen Build erhalten überschrieben wird tun , aber wir wollen , dass es Teil des Projekts so VS.Net gibt uns nicht das „Ihr Projekt ist nicht zum Debuggen konfiguriert“ Pop- oben.
  • Bearbeiten Sie Ihre .csprojProjektdatei und fügen Sie TransformXmldem AfterBuild-Ziel die folgende Aufgabe hinzu. Hier können Sie sehen, dass ich die web.base.configDatei mit dem transformiere web.[configuration].configund sie unter speichere web.config. Für Details klicken Sie bitte auf diesen Microsoft Q & A, sowie Anweisungen , wie die Build zu erweitern, suchen Sie es .

Option 2:

Basierend auf dieser Antwort habe ich eine einfache Konsolen-App entwickelt, TransformConfig.exe (in C # 6.0-Syntax):

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);
    }

  }
}

Stellen Sie sicher, dass Sie die DLL "C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\v14.0\Web\Microsoft.Web.XmlTransform.dll"als Referenz hinzufügen (dieses Beispiel gilt für VS 2015, bei älteren Versionen ersetzen Sie die v14.0im Pfad durch die entsprechende Versionsnummer, z v11.0. B. ).

Für Visual Studio 2017 hat sich das Namensschema für den Pfad geändert: Für die Unternehmensversion ist es beispielsweise hier : C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\MSBuild\Microsoft\VisualStudio\v15.0\Web.
Ich gehe davon aus, dass Sie für die Professional-Version Enterpriseim Pfad durch ersetzen müssen Professional. Wenn Sie die Vorschau-Version verwenden, ersetzen Sie diese zusätzlich 2017durch Preview.

Hier ein Überblick , wie der Weg für verschiedene Versionen von Visual Studio geändert hat (wenn Sie nicht über die Enterprise - Version haben könnten ersetzen müssen Enterprisedurch Professionalauf dem Weg):

VS Versionspfad         (für 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

Kompilieren Sie es und legen Sie die EXE-Datei in einem Verzeichnis ab, z C:\MyTools\.

Verwendung: Sie können es in Ihrem Post-Build-Ereignis verwenden ( wählen Sie in den Projekteigenschaften Build-Ereignisse aus und bearbeiten Sie die Befehlszeile für das Post-Build-Ereignis ). Befehlszeilenparameter sind (Beispiel):

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

dh zuerst den Namen der Konfigurationsdatei, gefolgt von der Transformationskonfigurationsdatei, gefolgt von einer optionalen Vorlagenkonfiguration, gefolgt vom Pfad zu Ihrem Projekt, das beide Dateien enthält.

Ich habe den optionalen Vorlagenkonfigurationsparameter hinzugefügt, da sonst Ihre ursprüngliche vollständige Konfiguration durch die Transformation überschrieben würde, was durch die Bereitstellung einer Vorlage vermieden werden kann.

Erstellen Sie die Vorlage, indem Sie einfach die ursprüngliche Web.config kopieren und Web.Template.config nennen.

Hinweis:

  • Wenn Sie möchten, können Sie die TransformConfig.exeDatei auch in den oben genannten Visual Studio-Pfad kopieren, in dem sich die Datei Microsoft.Web.XmlTransform.dllbefindet, und in all Ihren Projekten darauf verweisen, in denen Sie Ihre Konfigurationen transformieren müssen.

  • Für diejenigen unter Ihnen, die sich fragen, warum ich Environment.ExitCode = x;Zuweisungen hinzugefügt habe : Die einfache Rückgabe eines Int von Main hat beim Build-Ereignis nicht geholfen. Details finden Sie hier.

  • Wenn Sie Ihr Projekt veröffentlichen und Sie eine Web.Template.config verwenden, stellen Sie sicher , dass Sie eine haben wieder aufbauen auf Ihrer Lösung mit der richtigen Konfiguration ( in der Regel Release) , bevor Sie veröffentlichen. Der Grund dafür ist, dass die Web.Config beim Debuggen überschrieben wird und Sie möglicherweise die falsche Datei anderweitig transformieren.

Matt
quelle
1
Es sieht so aus, als ob der CodeProject-Beitrag pochiert ist. Er hat Screenshots für seine Codebeispiele verwendet, und jetzt, da sein Blog nicht mehr verfügbar ist, sind sie für die Geschichte verloren.
Eric Lloyd
3
Ja, leider sind die Screenshots weg. Aber zumindest ist der Artikeltext noch vorhanden, der den Ansatz beschreibt. Ich habe die Textbeschreibung zu meiner Antwort hinzugefügt, um zu vermeiden, dass sie verloren geht.
Matt
1
Es stimmt, vielleicht kann man versuchen, den Autor James Coleman bei codeproject zu kontaktieren, um es dort zu beheben. Ich bin mir jedoch nicht sicher, ob er dort noch aktiv ist. @ ThomasTeilmann
Matt
Ich denke, das könnte ähnlich sein wie in den verlorenen Screenshots. Scheint das gleiche grundlegende Ergebnis zu erzielen. stackoverflow.com/a/6437192/1003916
user1003916
22

Die Beantwortung Ihrer Frage ist nicht einfach, da sie ein Problem darstellt - wenn Sie Web.config mit Web.debug.config transformieren möchten - wo sollte der Transformationseffekt gespeichert werden? In Web.config selbst? Dies würde die Transformationsquelldatei überschreiben! Wahrscheinlich führt Visual Studio deshalb während der Erstellung keine Transformationen durch.

Die vorherige Antwort von Matt ist gültig, aber Sie können sie mischen, um eine generische Lösung zu erhalten, die funktioniert, wenn Sie die Konfiguration der aktiven Lösung tatsächlich von Debug auf Release usw. ändern. Hier ist eine einfache Lösung:

  1. Erstellen Sie Ihre Konfigurationstransformationen für Konfigurationen (Debug, Release usw.)
  2. Umbenennen - Web.configDatei Web.base.config- Transformationen sollten automatisch umbenennen entsprechend ( Web.base.Debug.configetc)
  3. Fügen Sie Folgendes hinzu Ihrem Projektordner die XML-Datei transformWebConfig.proj hinzu:
<?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. Navigieren Sie zu Ihren Projekteigenschaften und wählen Sie Ereignisse erstellen und fügen Sie der Befehlszeile für Ereignisse nach dem Erstellen folgenden Inhalt hinzu :
@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)

Wenn Sie jetzt Ihre Lösung erstellen, wird eine Web.config-Datei mit gültigen Transformationen für die aktive Konfiguration erstellt.

komsky
quelle
Sauberste und beste Antwort. Einige Fragen: 1. Warum besagt die XML-Validierung, dass das TransformXml-Element im Target-Element ungültig ist? (Der Build funktioniert übrigens). 2. Nachdem dies die echte Web.Config generiert hat, füge ich die Web.Config weiterhin zum Projekt hinzu. Jedes Mal, wenn ich zwischen Debug / Release wechsle, ändert sich die web.config, aber ich möchte das nicht unbedingt die ganze Zeit in das Quell-Repo übertragen.
Csaba Toth
1. Ich kann nicht wirklich sagen, wie VS dieses XML mit dem Schema validiert, aber diese Warnung ist häufig, sodass Sie sie ignorieren können. 2. Es hängt davon ab, welches Repo Sie verwenden, aber Sie können beispielsweise den Dateieintrag git.ignore verwenden.
Komsky
4
Das hat bei mir gut funktioniert - ich habe gerade die 12 in der Build-Event- und Proj-Datei auf die aktuelle Version geändert. Für die Post-Build - Ereignis verwendet I: '"$(MSBuildBinPath)\msbuild.exe" $(ProjectDir)TransformWebConfig.proj /t:TransformWebConfig /p:CurrentConfig=$(ConfigurationName) /p:TargetProjectName=$(TargetPath) und aktualisiert , v12.0um v14.0in der .proj Datei.
Jovie
1
Für VS 2017 ändern Sie jeden 12.0zu14.0
Csaba Toth
1
1) Vergessen Sie nicht, die generierte web.config in das Webprojekt aufzunehmen. Andernfalls wird sie nach der Veröffentlichung nicht in den Zielordner kopiert. 2) Wenn dem Build-Server diese beiden Dateien fehlen, kopieren Sie sie einfach auf den Server "Microsoft.Web.Publishing.Tasks", "Microsoft.Web.XmlTransform"
Phiree
7

für VS 2017 habe ich die Antwort gefunden hier nicht sicher, warum niemand oben darauf verwiesen hat, da es eine sehr beliebte Lösung zu sein scheint. Sehr einfach auch. Stellen Sie sicher, dass Sie den Kommentar von IOrlandoni am 5. März 2019 sehen, damit es in VS 2017 und allen Versionen funktioniert.

Grundsätzlich ist es ein Zwei-Schritt. Zuerst bearbeiten Sie die .csproj-Datei und fügen den folgenden Code hinzu. Zweitens erstellen Sie eine neue web.base.config-Konfiguration und kopieren dort die vorhandene web.config. Danach überschreibt jeder Build Ihre web.config mit der gewünschten Transformation.

<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>  
Tom McDonald
quelle
Dies ist wahrscheinlich die beste Antwort, aber IMO fehlt ein Trick. Wenn Sie Web.configvon Contentzu wechseln , Nonekönnen Sie Source="Web.config" Destination="$(TargetPath).config"(oder möglicherweise für einige Projekttypen Destination="$(TargetDir)Web.config") verwenden. Ich habe die Transformation auch nach verschoben AfterBuild, da sie nicht mehr ausgeführt werden muss, bevor Dateien kopiert werden.
Peter Taylor
Ok, eigentlich funktioniert das nicht, weil ich es aus irgendeinem Grund nicht für die Ausführung konfigurieren kann bin.
Peter Taylor
4

Ihre unmittelbare Frage wurde beantwortet - die Erklärung ist, dass die Transformation beim Veröffentlichen und nicht beim Erstellen angewendet wird.

Ich denke jedoch, dass es keine Lösung bietet, wie Sie das erreichen können, was Sie tun möchten.

Ich habe seit einigen Tagen mit genau diesem Problem zu kämpfen und nach einer Möglichkeit gesucht, web.config sauber zu halten und alle Schlüssel festzulegen, die je nach Umgebung in den jeweiligen Transformationsdateien variieren. Mein Fazit ist, dass die einfachste und stabilste Lösung darin besteht, Debug-Werte in der ursprünglichen web.config zu verwenden. Auf diese Weise sind sie immer vorhanden, wenn Sie Debug-Läufe in Visual Studio ausführen.

Erstellen Sie dann Transformationen für die verschiedenen Umgebungen, in denen Sie veröffentlichen möchten - Test, Integration, Produktion - was auch immer Sie haben. Hierfür reicht die jetzt integrierte Funktionalität zum Transformieren von web.config-Dateien beim Veröffentlichen aus. Keine Notwendigkeit für SlowCheetah oder das Bearbeiten von Build-Ereignissen oder Projektdateien. Wenn Sie nur Webprojekte haben, ist das.

Wenn Sie möchten, können Sie auch die Datei web.debug.config in Ihrer Lösung haben, um eine separate Datei mit allen Werten für die Entwicklungsumgebung zu erstellen. Stellen Sie sicher, dass die Werte bei der Ausführung in Visual Studio nicht angewendet werden, falls jemand anderes versucht, sie für diesen Zweck zu verwenden!

Carolina Persson
quelle
1

Verwenden Sie Octopus Deploy (Community Edition ist kostenlos) und lassen Sie es das web.configfür Sie transformieren . Schritte:

  1. Richten Sie Octopus für die Bereitstellung Ihrer Webanwendung ein
  2. Stellen Sie sicher, dass für Sie Web.Release.configdie Build ActionEigenschaft Contentgenau wie für Ihre Hauptdatei festgelegt web.configist.

Das ist es! Octopus erledigt den Rest ohne spezielle Konfiguration. Bei einer Standardbereitstellung der IIS-Website wird dies sofort ausgeführt:Geben Sie hier die Bildbeschreibung ein

Matt Kocaj
quelle
Nummer 2 ist der Schlüssel :)
Reza
0

Anscheinend gibt es eine Erweiterung für Visual Studio 2015

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

Mit diesem Paket können Sie Ihre app.config oder eine andere XML-Datei basierend auf der Build-Konfiguration transformieren

Amir Astaneh
quelle
SlowCheeta ist nicht neu. es ist schon lange da. Ihre 1.1 Veröffentlichung war am 08.09.2011
HaBo
@ HaBo Vielen Dank für Ihre Aufmerksamkeit, ich habe die newWelt als Satz entfernt.
Amir Astaneh
0

Vor kurzem hatte ich das gleiche Problem mit einer älteren web.config- Datei, die auf .NET Framework 2.0 basiert. Die Lösung wurde entfernen Sie einfach den web.config Namespace ( xmlns in attibute Konfiguration root node):

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

NACH DEM: <configuration>

Rafael Neto
quelle