La transformation Web Config ne fonctionne pas

88

Dans une application .NET MVC 3.0, j'ai la configuration suivante dans 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>

Pour le débogage, j'ai la transformation de configuration suivante définie:

web.Debug.config

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

Et j'exécute l'application en mode débogage, mais mon port SMTP prend toujours la valeur du web.config, non web.Debug.config.

Quelqu'un peut-il suggérer ce qui ne va pas dans cette configuration?

HaBo
la source

Réponses:

156

Les transformations Web.config ne sont appliquées que dans le cadre d'une opération de publication.

Si vous souhaitez que cela soit fait dans le cadre d'une app.configopération de génération, vous pouvez utiliser le plugin SlowCheetah - XML ​​Transforms Visual Studio:

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

devdigital
la source
1
Merci beaucoup, vous m'avez fait gagner beaucoup de temps.
HaBo
3
wow m'a pris 2 heures pour trouver cette réponse. Merci de l'avoir posté, j'aurais été arraché les cheveux.
Peanut
Il semble que dans Visual Studio 2015 (Web), les transformations .config sont désormais une fonctionnalité intégrée, vous n'avez donc plus besoin de SlowCheetah. Mais les transformations intégrées ne s'appliqueront que si vous publiez l'application, pas si vous l'exécutez. Vous pouvez regarder ici comment je l'ai résolu.
Matt
1
Je ne sais pas pourquoi utiliser cela alors que la réponse de @ komsky fournit une solution simple et propre.
Csaba Toth
1
SlowCheetah est génial, mais d'après sa propre documentation: "Pour les projets Web, les fichiers sont transformés lorsque vous publiez ou empaquetez votre application." En d'autres termes, pas pendant le débogage.
Doug
31

Visual Studio (2010-2019) ne le prend malheureusement pas directement en charge pendant le débogage, il est uniquement destiné à la publication - même avec l'extension SlowCheetah (réponse marquée) cela ne fonctionne pas pour moi (uniquement pour les projets utilisant app.config plutôt que web.config).

Notez qu'il existe une solution de contournement décrite dans codeproject .

Il décrit comment modifier le fichier .msproj pour remplacer le fichier web.config actuel par la version transformée.

Je vais d'abord décrire cette solution de contournement comme l' option 1 , mais j'ai récemment découvert une autre option 2 , qui est plus facile à utiliser (vous pouvez donc faire défiler jusqu'à l'option 2 directement si vous le souhaitez):


Option 1: J'ai ajouté les instructions tirées de l' article d' origine du projet de code (voir le lien ci-dessus), car les captures d'écran y sont déjà parties et je ne veux pas perdre toutes les informations:

VS.Net n'effectue aucune transformation lorsque vous développez et déboguez simplement votre environnement local. Mais si vous le souhaitez, vous pouvez suivre certaines étapes pour y parvenir.

  • Tout d'abord, créez les configurations souhaitées dans VS.Net , en supposant que le débogage et la publication par défaut ne suffisent pas pour ce que vous essayez d'accomplir.
  • Cliquez avec le bouton droit sur votre web.configet sélectionnez Ajouter des transformations de configuration - cela créera une configuration de transformation dépendante pour chacune de vos configurations définies.
  • Vous pouvez maintenant renommer votre web.configen web.base.config.
  • Ajoutez un web.configà votre projet. Peu importe ce qui est en elle , car elle écrasés chaque fois que nous compilons , mais nous voulons qu'elle partie du projet afin VS.Net ne nous donne pas le « Votre projet n'est pas configuré pour le débogage » pop- vers le haut.
  • Modifiez votre .csprojfichier de projet et ajoutez la TransformXmltâche suivante à la cible AfterBuild. Ici, vous pouvez voir que je vais transformer le web.base.configfichier en utilisant le web.[configuration].configet il l'enregistrera sous web.config. Pour plus de détails, veuillez consulter ce Microsoft Q&A et pour savoir comment étendre la version, regardez .

Option 2:

Sur la base de cette réponse, j'ai développé une application console simple, TransformConfig.exe (dans la syntaxe 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);
    }

  }
}

Assurez-vous d'ajouter la DLL "C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\v14.0\Web\Microsoft.Web.XmlTransform.dll"comme référence (cet exemple s'applique à VS 2015, pour les anciennes versions, remplacez le v14.0dans le chemin par le numéro de version approprié, par exemple v11.0).

Pour Visual Studio 2017, le schéma de nommage pour le chemin a changé: Par exemple, pour la version entreprise est ici: C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\MSBuild\Microsoft\VisualStudio\v15.0\Web.
Je suppose que pour la version professionnelle, vous devez remplacer Enterprisele chemin par Professional. Si vous utilisez la version préliminaire, remplacez-la également 2017par Preview.

Voici un aperçu de la façon dont le chemin a changé pour différentes versions de Visual Studio (si vous ne disposez pas de la version Entreprise, vous devrez peut-être remplacer Enterprisepar Professionaldans le chemin):

        Chemin de la Microsoft.Web.XmlTransform.dllversion VS (pour )
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

Compilez-le et placez le fichier .exe dans un répertoire, par exemple C:\MyTools\.

Utilisation: vous pouvez l'utiliser dans votre événement post-build (dans les propriétés du projet , sélectionnez Événements de génération , puis modifiez la ligne de commande de l'événement post-build ). Les paramètres de ligne de commande sont (exemple):

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

c'est-à-dire d'abord le nom du fichier de configuration, suivi du fichier de configuration de transformation, suivi d'un modèle de configuration optionnel, suivi du chemin vers votre projet contenant les deux fichiers.

J'ai ajouté le paramètre de configuration de modèle facultatif, car sinon votre configuration complète d'origine serait écrasée par la transformation, ce qui peut être évité en fournissant un modèle.

Créez le modèle en copiant simplement le Web.config d'origine et nommez-le Web.Template.config.

Remarque:

  • Si vous préférez, vous pouvez également copier le TransformConfig.exefichier dans le chemin de Visual Studio mentionné ci-dessus où Microsoft.Web.XmlTransform.dllréside le et y faire référence dans tous vos projets où vous devez transformer vos configurations.

  • Pour ceux d'entre vous qui se demandent pourquoi j'ai ajouté des Environment.ExitCode = x;affectations: Le simple fait de renvoyer un int depuis Main n'a pas aidé lors de l'événement de construction. Voir les détails ici.

  • Si vous publiez votre projet et que vous utilisez un Web.Template.config, assurez -vous que vous avez fait une reconstruction sur votre solution avec la bonne configuration (généralement Release) avant de publier. La raison en est que Web.Config est écrasé pendant le débogage et que vous pourriez finir par transformer le mauvais fichier dans le cas contraire.

Mat
la source
1
On dirait que le message CodeProject est caboté. Il a utilisé des captures d'écran pour ses échantillons de code, et maintenant que son blog est en panne, ils sont perdus dans l'histoire.
Eric Lloyd
3
Oui, malheureusement, les captures d'écran ont disparu. Mais au moins le texte de l'article est toujours là, décrivant l'approche. J'ai ajouté la description textuelle à ma réponse pour éviter de la perdre.
Matt
1
Certes, on peut peut-être essayer de contacter l'auteur James Coleman de codeproject pour y remédier. Je ne sais pas s'il y est toujours actif. @ThomasTeilmann
Matt
Je pense que cela pourrait être similaire à ce qui était dans les captures d'écran perdues. Semble atteindre le même résultat de base. stackoverflow.com/a/6437192/1003916
user1003916
22

Répondre à votre question n'est pas simple, car cela pose un problème - si vous voulez transformer Web.config avec Web.debug.config - où l'effet de transformation doit être stocké? Dans Web.config lui-même? Cela écraserait le fichier source de la transformation! C'est probablement pourquoi Visual Studio n'effectue pas de transformations pendant les builds.

La réponse précédente de Matt est valide, mais vous voudrez peut-être les mélanger pour avoir une solution générique qui fonctionne lorsque vous modifiez réellement la configuration de la solution active du débogage à la version, etc. Voici une solution simple:

  1. Créez vos transformations de configuration pour les configurations (Debug, Release, etc.)
  2. Renommer le Web.configfichier en Web.base.config- les transformations doivent automatiquement renommer en conséquence ( Web.base.Debug.config, etc.)
  3. Ajoutez le fichier XML transformWebConfig.proj suivant à votre dossier de projet:
<?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. Accédez aux propriétés de votre projet, choisissez Créer des événements et ajoutez le contenu suivant à la ligne de commande de l'événement post-construction :
@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)

Désormais, lorsque vous générez votre solution, un fichier Web.config sera créé avec des transformations valides pour la configuration active.

Komsky
la source
Réponse la plus propre et la meilleure. Quelques questions: 1. Pourquoi les validations XML indiquent que l'élément TransformXml n'est pas valide dans l'élément Target? (la construction fonctionne BTW). 2. Maintenant que cela génère le vrai Web.Config, j'ajoute toujours le Web.Config au projet. Désormais, chaque fois que je basculerai entre Debug / Release, le web.config changera, mais je ne veux pas nécessairement le valider tout le temps dans le dépôt source.
Csaba Toth
1. Je ne peux pas vraiment dire comment VS valide ce XML avec le schéma, mais cet avertissement est courant, vous pouvez donc l'ignorer. 2. Cela dépend du dépôt que vous utilisez, mais vous pouvez par exemple utiliser l'entrée de fichier git.ignore.
komsky
4
Cela a bien fonctionné pour moi - je viens de changer les 12 dans le fichier build-event et proj à la version actuelle. Pour l'événement post-build que j'ai utilisé: '"$(MSBuildBinPath)\msbuild.exe" $(ProjectDir)TransformWebConfig.proj /t:TransformWebConfig /p:CurrentConfig=$(ConfigurationName) /p:TargetProjectName=$(TargetPath) et mis v12.0à jour v14.0dans le fichier .proj.
Jovie
1
Pour VS 2017, modifiez tous les 12.0à14.0
Csaba Toth
1
1) n'oubliez pas d'inclure le web.config généré dans le projet Web, sinon il ne sera pas copié dans le dossier cible après sa publication. 2) si le serveur de build manque de ces deux fichiers, copiez-les simplement sur le serveur "Microsoft.Web.Publishing.Tasks", "Microsoft.Web.XmlTransform"
phiree
8

pour VS 2017, j'ai trouvé la réponse ici, je ne sais pas pourquoi personne ne l'a référencé ci-dessus car il semble être une solution très populaire. Très facile aussi. Assurez-vous de voir le commentaire d'IOrlandoni le 5 mars 2019 pour le faire fonctionner dans VS 2017 et toutes les versions.

Fondamentalement, c'est un deux pas à pas. Tout d'abord, vous modifiez le fichier .csproj, en ajoutant le code ci-dessous. Deuxièmement, vous créez une nouvelle configuration web.base.config et y copiez le web.config existant. Après cela, toute construction écrasera votre web.config avec la transformation souhaitée.

<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
la source
C'est probablement la meilleure réponse, mais l'OMI manque un truc. Si vous passez Web.configde Contentà, Nonevous pouvez utiliser Source="Web.config" Destination="$(TargetPath).config"(ou peut-être pour certains types de projet, Destination="$(TargetDir)Web.config"). J'ai également déplacé la transformation vers AfterBuild, car elle n'a plus besoin d'être effectuée avant la copie des fichiers.
Peter Taylor
Ok, en fait cela ne fonctionne pas parce que pour une raison quelconque, je ne peux pas le configurer pour qu'il s'exécute bin.
Peter Taylor
4

Votre question immédiate a reçu une réponse - l'explication est que la transformation est appliquée lors de la publication et non lors de la génération.

Cependant, je pense qu'il n'offre pas de solution sur la façon de réaliser ce que vous voulez faire.

Cela fait quelques jours que je lutte avec ce problème exact, à la recherche d'un moyen de garder web.config propre et de définir toutes les clés qui varient en fonction de l'environnement dans les fichiers de transformation respectifs. Ma conclusion est que la solution la plus simple et la plus stable consiste à utiliser des valeurs de débogage dans le web.config d'origine, de cette façon elles sont toujours présentes lorsque vous effectuez des exécutions de débogage dans Visual Studio.

Créez ensuite des transformations pour les différents environnements dans lesquels vous souhaitez publier - test, intégration, production - tout ce que vous avez. La fonctionnalité désormais intégrée pour transformer les fichiers web.config lors de la publication suffira pour cela. Pas besoin de SlowCheetah ni de modification des événements de construction ni des fichiers de projet. Si vous n'avez que des projets Web, c'est.

Si vous le souhaitez, vous pouvez également avoir le fichier web.debug.config dans votre solution, juste pour conserver un fichier séparé avec toutes les valeurs relatives à l'environnement de développement. Assurez-vous de commenter que les valeurs ne sont pas appliquées lors de l'exécution dans Visual Studio, au cas où quelqu'un d'autre essayerait de l'utiliser à cette fin!

Carolina Persson
la source
1

Utilisez Octopus Deploy (l'édition Community est gratuite) et laissez-le transformer le web.configpour vous. Pas:

  1. Configurez Octopus pour déployer votre application Web
  2. Assurez-vous que votre propriété Web.Release.configa la même valeur que votre fichier principal .Build ActionContentweb.config

C'est ça! Octopus fera le reste sans aucune configuration particulière. Un déploiement de site Web IIS par défaut le fera immédiatement:entrez la description de l'image ici

Matt Kocaj
la source
Le numéro 2 est la clé :)
Reza
0

Apparemment, il existe une extension pour Visual Studio 2015

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

Ce package vous permet de transformer votre app.config ou tout autre fichier XML en fonction de la configuration de construction

Amir Astaneh
la source
SlowCheeta n'est pas nouveau. il existe depuis longtemps. Leur version 1.1 était en 9/8/2011
HaBo
@HaBo Merci pour votre attention, j'ai supprimé le newmonde comme phrase.
Amir Astaneh
0

Récemment, j'ai eu le même problème avec un ancien fichier web.config basé sur .NET Framework 2.0. La solution était simplement de supprimer l'espace de noms de web.config ( attibute xmlns dans le nœud racine de configuration ):

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

APRÈS: <configuration>

Rafael Neto
la source