Transformasi Konfigurasi Web tidak berfungsi

88

Dalam Aplikasi .NET MVC 3.0 saya memiliki konfigurasi berikut di 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>

Untuk debugging, saya memiliki transformasi konfigurasi berikut yang ditentukan:

web.Debug.config

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

Dan saya menjalankan aplikasi dalam mode debug, tetapi port SMTP saya masih mengambil nilai dari web.config, bukan web.Debug.config.

Adakah yang bisa menyarankan apa yang bisa salah dalam konfigurasi ini?

HaBo
sumber

Jawaban:

157

Transformasi Web.config hanya diterapkan sebagai bagian dari operasi publikasi.

Jika Anda ingin ini dilakukan sebagai bagian dari app.configoperasi build, Anda dapat menggunakan plugin SlowCheetah - XML ​​Transforms Visual Studio:

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

devdigital
sumber
1
Terima kasih banyak, Anda telah menghemat banyak waktu saya.
HaBo
3
wow butuh waktu 2 jam untuk menemukan jawaban ini. Terima kasih telah mempostingnya. Saya akan menarik rambut saya.
Peanut
Tampaknya dalam Visual Studio 2015 (Web) .config transforms sekarang sudah menjadi fitur built-in, jadi Anda tidak lagi membutuhkan SlowCheetah. Tetapi transformasi bawaan hanya akan berlaku jika Anda menerbitkan aplikasi, bukan jika Anda menjalankannya. Anda dapat melihat di sini bagaimana saya menyelesaikannya.
Matt
1
Tidak yakin mengapa menggunakan ini sementara jawaban @ komsky memberikan solusi yang sederhana dan bersih.
Csaba Toth
2
SlowCheetah memang bagus, tetapi dari dokumentasinya sendiri: "Untuk proyek web, file diubah saat Anda menerbitkan atau mengemas aplikasi Anda." Dengan kata lain, tidak saat debugging.
Doug
33

Visual Studio (2010-2019) sayangnya tidak secara langsung mendukungnya saat Anda melakukan debug, ini hanya dimaksudkan untuk penerbitan - bahkan dengan ekstensi SlowCheetah (jawaban yang ditandai) itu tidak berfungsi untuk saya (hanya untuk proyek yang menggunakan app.config daripada web.config).

Perhatikan bahwa ada solusi yang dijelaskan di codeproject .

Ini menjelaskan cara memodifikasi file .msproj untuk menimpa web.config saat ini dengan versi yang diubah.

Pertama-tama saya akan menjelaskan solusi tersebut sebagai Opsi 1 , tetapi baru-baru ini saya menemukan Opsi 2 lain , yang lebih mudah digunakan (jadi Anda dapat menggulir ke bawah ke opsi 2 secara langsung jika Anda mau):


Opsi 1: Saya telah menambahkan instruksi yang diambil dari artikel codeproject asli (lihat tautan di atas), karena tangkapan layar di sana sudah hilang, dan saya tidak ingin kehilangan seluruh informasi:

VS.Net tidak melakukan transformasi apa pun saat Anda mengembangkan dan hanya men-debug lingkungan lokal Anda. Tetapi ada beberapa langkah yang dapat Anda lakukan untuk mewujudkannya jika Anda mau.

  • Pertama, buat konfigurasi yang Anda inginkan di VS.Net , dengan asumsi debug dan rilis default tidak cukup untuk apa yang ingin Anda capai.
  • Klik kanan pada Anda web.configdan pilih Add Config Transforms - ini akan membuat konfigurasi transformasi dependen untuk setiap konfigurasi yang Anda tentukan.
  • Sekarang Anda dapat mengubah nama web.configmenjadi web.base.config.
  • Tambahkan web.configke proyek Anda. Tidak masalah apa yang ada di dalamnya karena itu akan ditimpa setiap kali kita melakukan pembangunan tetapi kita menginginkannya menjadi bagian dari proyek sehingga VS.Net tidak memberi kita pop- "Proyek Anda tidak dikonfigurasi untuk Debugging" naik.
  • Edit .csprojFile Proyek Anda dan tambahkan TransformXmltugas berikut ke target AfterBuild. Di sini Anda dapat melihat saya akan mengubah web.base.configfile menggunakan web.[configuration].configdan akan menyimpannya sebagai web.config. Untuk detailnya, periksa Q&A Microsoft ini , dan untuk instruksi bagaimana memperluas build, lihat di sana .

Pilihan 2:

Berdasarkan jawaban ini , saya telah mengembangkan aplikasi konsol sederhana, TransformConfig.exe (dalam sintaks 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);
    }

  }
}

Pastikan bahwa Anda menambahkan DLL "C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\v14.0\Web\Microsoft.Web.XmlTransform.dll"sebagai referensi (contoh ini berlaku untuk VS 2015, untuk versi yang lebih lama ganti v14.0di jalur dengan nomor versi yang sesuai, mis.v11.0 ).

Untuk Visual Studio 2017, skema penamaan jalan telah berubah: Misalnya, untuk versi enterprise itu adalah di sini: C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\MSBuild\Microsoft\VisualStudio\v15.0\Web.
Saya berasumsi bahwa untuk versi profesional Anda perlu mengganti Enterprisedi jalur dengan Professional. Jika Anda menggunakan versi pratinjau, ganti 2017dengan Preview.

Berikut gambaran bagaimana jalan telah berubah untuk berbagai versi Visual Studio (jika Anda tidak memiliki versi Enterprise Anda mungkin perlu mengganti Enterprisedengan Professionaldi jalan):

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

Kompilasikan dan letakkan file .exe ke dalam direktori, mis C:\MyTools\.

Penggunaan: Anda dapat menggunakannya dalam acara pasca-pembuatan (di properti proyek , pilih Peristiwa Build , lalu edit baris perintah Peristiwa pasca-pembangunan ). Parameter baris perintah adalah (contoh):

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

yaitu, pertama nama file konfigurasi, diikuti oleh file konfigurasi transformasi, diikuti oleh konfigurasi template opsional, diikuti dengan jalur ke proyek Anda yang berisi kedua file tersebut.

Saya telah menambahkan parameter konfigurasi template opsional karena jika tidak, konfigurasi lengkap asli Anda akan ditimpa oleh transformasi, yang dapat dihindari dengan menyediakan template.

Buat template hanya dengan menyalin Web.config asli dan beri nama Web.Template.config.

catatan:

  • Jika Anda mau, Anda juga dapat menyalin TransformConfig.exefile ke jalur Visual Studio yang disebutkan di atas tempat Microsoft.Web.XmlTransform.dlltinggal dan merujuknya di semua proyek Anda di mana Anda perlu mengubah konfigurasi Anda.

  • Bagi Anda yang bertanya-tanya mengapa saya menambahkan Environment.ExitCode = x;tugas: Hanya mengembalikan int dari Utama tidak membantu dalam acara pembangunan. Lihat detailnya di sini.

  • Jika Anda menerbitkan proyek Anda dan Anda menggunakan Web.Template.config, pastikan Anda melakukan rekondisi pada solusi Anda dengan konfigurasi yang benar (biasanya Rilis) sebelum Anda menerbitkan. Alasannya adalah Web.Config ditimpa selama proses debug dan Anda mungkin akan mengubah file yang salah jika tidak.

Matt
sumber
1
Sepertinya kiriman CodeProject itu bermasalah. Dia menggunakan tangkapan layar untuk contoh kodenya, dan sekarang karena blognya tidak aktif, mereka hilang dari sejarah.
Eric Lloyd
3
Ya, sayangnya tangkapan layarnya hilang. Tapi setidaknya teks artikel masih ada, menggambarkan pendekatannya. Saya telah menambahkan deskripsi teks ke jawaban saya untuk menghindari kehilangannya.
Matt
1
Benar, mungkin seseorang dapat mencoba menghubungi penulis James Coleman di codeproject untuk memperbaikinya di sana. Tidak yakin apakah dia masih aktif di sana. @ThomasTeilmann
Matt
Saya pikir ini mungkin mirip dengan apa yang ada di tangkapan layar yang hilang. Tampaknya mencapai hasil dasar yang sama. stackoverflow.com/a/6437192/1003916
user1003916
22

Menjawab pertanyaan Anda tidaklah mudah, karena ini menimbulkan masalah - jika Anda ingin mengubah Web.config dengan Web.debug.config - di mana efek transformasi harus disimpan? Di Web.config itu sendiri? Ini akan menimpa file sumber transformasi! Mungkin itu sebabnya Visual Studio tidak melakukan transformasi selama pembuatan.

Jawaban Matt sebelumnya valid, tetapi Anda mungkin ingin mencampurnya untuk mendapatkan solusi umum yang berfungsi saat Anda benar-benar mengubah konfigurasi solusi aktif dari debug ke rilis, dll. Berikut solusi sederhana:

  1. Buat transformasi konfigurasi Anda untuk konfigurasi (Debug, Rilis, dll)
  2. Ubah nama Web.configfile menjadi Web.base.config- transformasi harus mengganti nama secara otomatis ( Web.base.Debug.config, dll)
  3. Tambahkan file XML transformWebConfig.proj berikut ke folder proyek Anda:
<?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. Arahkan ke properti proyek Anda, pilih Build Events dan tambahkan konten berikut ke baris perintah Post-build event :
@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)

Sekarang, ketika Anda membangun solusi Anda, file Web.config akan dibuat dengan transformasi yang valid untuk konfigurasi aktif.

komsky
sumber
Jawaban terbersih dan terbaik. Beberapa pertanyaan: 1. Mengapa validasi XML mengatakan bahwa elemen TransformXml tidak valid dalam elemen Target? (build berfungsi BTW). 2. Sekarang ini menghasilkan Web.Config yang sebenarnya, saya masih menambahkan Web.Config ke proyek. Sekarang setiap kali saya beralih antara Debug / Rilis, web.config akan berubah, tetapi saya tidak ingin selalu memasukkan itu ke dalam repo sumber.
Csaba Toth
1. Tidak tahu bagaimana VS memvalidasi XML ini dengan skema, tapi peringatan ini biasa terjadi, jadi Anda bisa mengabaikannya. 2. Tergantung repo apa yang Anda gunakan, tetapi Anda dapat menggunakan entri file git.ignore, misalnya.
komsky
4
Ini bekerja dengan baik untuk saya - baru saja mengubah 12 di build-event dan file proj ke versi saat ini. Untuk acara pasca-pembangunan saya menggunakan: '"$(MSBuildBinPath)\msbuild.exe" $(ProjectDir)TransformWebConfig.proj /t:TransformWebConfig /p:CurrentConfig=$(ConfigurationName) /p:TargetProjectName=$(TargetPath) dan diperbarui v12.0ke v14.0dalam file .proj.
Jovie
1
Untuk VS 2017 modifikasi setiap 12.0ke14.0
Csaba Toth
1
1) jangan lupa untuk menyertakan web.config yang dihasilkan ke dalam proyek web, atau, itu tidak akan disalin ke folder target setelah diterbitkan. 2) jika server build kekurangan dua file ini, cukup salin ke server "Microsoft.Web.Publishing.Tasks", "Microsoft.Web.XmlTransform"
phiree
8

untuk VS 2017 saya menemukan jawabannya disini tidak yakin mengapa tidak ada yang mereferensikannya di atas karena tampaknya menjadi solusi yang sangat populer. Sangat mudah juga. Pastikan Anda melihat komentar dari IOrlandoni pada Mar5 2019 untuk membuatnya berfungsi di VS 2017 dan semua versi.

Pada dasarnya ini adalah dua langkah. Pertama, Anda mengedit file .csproj, menambahkan kode di bawah ini. Kedua, Anda membuat konfigurasi web.base.config baru dan menyalin web.config yang ada di sana. Setelah melakukan itu, build apa pun akan menimpa web.config Anda dengan transformasi yang Anda inginkan.

<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
sumber
Ini mungkin jawaban terbaik, tapi IMO melewatkan satu trik. Jika Anda mengubah Web.configdari Contentke Nonemaka Anda dapat menggunakan Source="Web.config" Destination="$(TargetPath).config"(atau mungkin untuk beberapa jenis proyek, Destination="$(TargetDir)Web.config"). Saya juga memindahkan transformasi ke AfterBuild, karena tidak perlu lagi dilakukan sebelum file disalin.
Peter Taylor
Ok, sebenarnya itu tidak berhasil karena untuk beberapa alasan saya tidak dapat mengkonfigurasinya untuk dijalankan bin.
Peter Taylor
5

Pertanyaan langsung Anda telah terjawab - penjelasannya adalah bahwa transformasi diterapkan pada publikasi, bukan pada versi build.

Namun, saya pikir itu tidak menawarkan solusi tentang bagaimana mencapai apa yang ingin Anda lakukan.

Saya telah berjuang dengan masalah yang tepat ini selama beberapa hari sekarang, mencari cara untuk menjaga web.config bersih dan mengatur semua kunci yang bervariasi tergantung pada lingkungan di masing-masing file transformasi. Kesimpulan saya adalah bahwa solusi termudah dan paling stabil adalah dengan menggunakan nilai debug di web.config asli, dengan cara itu mereka selalu ada saat Anda menjalankan debug di Visual Studio.

Kemudian buat transformasi untuk lingkungan berbeda yang ingin Anda publikasikan - pengujian, integrasi, produksi - apa pun yang Anda miliki. Fungsionalitas bawaan sekarang untuk mengubah file web.config saat dipublikasikan sudah cukup untuk ini. Tidak perlu SlowCheetah atau mengedit acara pembangunan atau file proyek. Jika Anda hanya memiliki proyek web.

Jika Anda ingin, Anda juga dapat memiliki file web.debug.config dalam solusi Anda, hanya untuk menyimpan file terpisah dengan semua nilai yang berkaitan dengan lingkungan pengembangan. Pastikan untuk berkomentar di dalamnya bahwa nilai tidak diterapkan saat menjalankan Visual Studio, jika orang lain mencoba menggunakannya untuk tujuan itu!

Carolina Persson
sumber
1

Gunakan Octopus Deploy (Edisi komunitas gratis) dan biarkan itu mentransformasikannya web.configuntuk Anda. Langkah:

  1. Siapkan Octopus untuk menerapkan aplikasi web Anda
  2. Pastikan Anda Web.Release.configmemiliki Build Actionproperti yang disetel Contentseperti web.configfile utama Anda .

Itu dia! Gurita akan melakukan sisanya tanpa konfigurasi khusus. Penyebaran situs Web IIS default akan melakukan ini di luar kotak:masukkan deskripsi gambar di sini

Matt Kocaj
sumber
Nomor 2 adalah kuncinya :)
Reza
0

Baru-baru ini saya mengalami masalah yang sama dengan file web.config lama berdasarkan .NET Framework 2.0. Solusinya cukup dengan menghapus namespace web.config ( xmlns attibute di node root konfigurasi ):

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

SETELAH: <configuration>

Rafael Neto
sumber