Variabel dalam app.config / web.config

92

Apakah mungkin untuk melakukan sesuatu seperti berikut di file app.configatau web.config?

<appSettings>
 <add key="MyBaseDir" value="C:\MyBase" />
 <add key="Dir1" value="[MyBaseDir]\Dir1"/>
 <add key="Dir2" value="[MyBaseDir]\Dir2"/>
</appSettings>

Saya kemudian ingin mengakses Dir2 di kode saya hanya dengan mengatakan:

 ConfigurationManager.AppSettings["Dir2"]

Ini akan membantu saya ketika saya menginstal aplikasi saya di server dan lokasi yang berbeda di mana saya hanya perlu mengubah SATU entri di seluruh saya app.config. (Saya tahu saya bisa mengatur semua penggabungan dalam kode, tapi saya lebih suka dengan cara ini).

DeeStackOverflow
sumber
Saya pikir dia berbicara tentang mendefinisikan variabel untuk digunakan dalam kunci appSettings langsung di dalam file konfigurasi.
Michaël Carpentier
1
Saya juga memeriksa menggunakan deklarasi XML <! ENTITY>, tetapi tidak didukung karena cara MS menangani file web.config.
chilltemp
Terima kasih atas usahamu. Saya memilih untuk tidak mengubah kode apa pun. Kode sudah memiliki pernyataan yang mengatakan: string dir2 = ConfigurationManager.AppSettings ["Dir2"]. Saya hanya ingin membersihkan app.config yang sekarang mengatakan value = "D: \ blahdir \ Dir2" bukan value = "[MyBaseDir] \ Dir2"
DeeStackOverflow

Jawaban:

7

Pertanyaan bagus.

Saya rasa tidak ada. Saya percaya ini akan cukup terkenal jika ada cara yang mudah, dan saya melihat bahwa Microsoft sedang membuat mekanisme dalam Visual Studio 2010 untuk menyebarkan file konfigurasi yang berbeda untuk penyebaran dan pengujian.

Namun demikian; Saya telah menemukan bahwa Anda di ConnectionStringsbagian ini memiliki semacam placeholder yang disebut "| DataDirectory |". Mungkin Anda bisa melihat apa yang sedang dikerjakan di sana ...

Ini adalah bagian dari machine.configmenunjukkannya:

 <connectionStrings>
    <add
        name="LocalSqlServer"
        connectionString="data source=.\SQLEXPRESS;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|aspnetdb.mdf;User Instance=true"
        providerName="System.Data.SqlClient"
    />
 </connectionStrings>
Arjan Einbu
sumber
Itu informasi yang menarik. Mungkin variabel diakses menggunakan simbol pipa ("|")? Hmm .. Saya ingin tahu apakah ini akan berhasil: <add key = "Dir2" value = "| MyBaseDir | \ Dir2" />
DeeStackOverflow
4
Nilai DataDirectory sebenarnya adalah elemen data di AppDomain. Anda dapat mengganti nilai dengan menggunakan AppDomain.CurrentDomain.SetData ("DataDirectory", dataPath); Saya belum menguji apakah Anda dapat menentukan variabel lain seperti ini dan membuatnya "diperluas otomatis" ...
Peter Lillevold
22

Alternatif yang sedikit lebih rumit, tetapi jauh lebih fleksibel, adalah membuat kelas yang mewakili bagian konfigurasi. Di app.config/ web.configfile Anda, Anda dapat memiliki ini:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <!-- This section must be the first section within the <configuration> node -->
    <configSections>
        <section name="DirectoryInfo" type="MyProjectNamespace.DirectoryInfoConfigSection, MyProjectAssemblyName" />
    </configSections>

    <DirectoryInfo>
        <Directory MyBaseDir="C:\MyBase" Dir1="Dir1" Dir2="Dir2" />
    </DirectoryInfo>
</configuration>

Kemudian, dalam kode .NET Anda (saya akan menggunakan C # dalam contoh saya), Anda dapat membuat dua kelas seperti ini:

using System;
using System.Configuration;

namespace MyProjectNamespace {

    public class DirectoryInfoConfigSection : ConfigurationSection {

        [ConfigurationProperty("Directory")]
        public DirectoryConfigElement Directory {
            get {
                return (DirectoryConfigElement)base["Directory"];
            }
    }

    public class DirectoryConfigElement : ConfigurationElement {

        [ConfigurationProperty("MyBaseDir")]
        public String BaseDirectory {
            get {
                return (String)base["MyBaseDir"];
            }
        }

        [ConfigurationProperty("Dir1")]
        public String Directory1 {
            get {
                return (String)base["Dir1"];
            }
        }

        [ConfigurationProperty("Dir2")]
        public String Directory2 {
            get {
                return (String)base["Dir2"];
            }
        }
        // You can make custom properties to combine your directory names.
        public String Directory1Resolved {
            get {
                return System.IO.Path.Combine(BaseDirectory, Directory1);
            }
        }
    }
}

Terakhir, dalam kode program Anda, Anda dapat mengakses app.configvariabel Anda , menggunakan kelas baru Anda, dengan cara ini:

DirectoryInfoConfigSection config =
  (DirectoryInfoConfigSection)ConfigurationManager.GetSection("DirectoryInfo");
String dir1Path = config.Directory.Directory1Resolved;  // This value will equal "C:\MyBase\Dir1"
Matt Hamsmith
sumber
1
Terima kasih, tetapi saya mencoba melakukan ini tanpa mengubah kode apa pun karena ini menyebalkan pada tahap ini.
DeeStackOverflow
Ada kesalahan kecil di baris terakhir kode (tidak termasuk tanda kurung): "return System.IO.Path.Combine (MyBaseDir, Dir1);" sebagai gantinya harus "return System.IO.Path.Combine (BaseDirectory, Dir1);", atau metode lainnya harus diganti dari 'Base Directory' menjadi 'MyBaseDir'
TheWho
16

Anda dapat menyelesaikannya menggunakan perpustakaan saya yang Luas . Juga tersedia di nuget disini .

Ini dirancang dengan ini sebagai kasus penggunaan utama.

Contoh Sedang (menggunakan AppSettings sebagai sumber default untuk perluasan token)

Di app.config:

<configuration>
    <appSettings>
        <add key="Domain" value="mycompany.com"/>
        <add key="ServerName" value="db01.{Domain}"/>
    </appSettings>
    <connectionStrings>
        <add name="Default" connectionString="server={ServerName};uid=uid;pwd=pwd;Initial Catalog=master;" provider="System.Data.SqlClient" />
    </connectionStrings>
</configuration>

Gunakan metode ekstensi .Expand () pada string yang akan diperluas:

var connectionString = ConfigurationManager.ConnectionStrings["Default"].ConnectionString;
connectionString.Expand() // returns "server=db01.mycompany.com;uid=uid;pwd=pwd;Initial Catalog=master;"

atau

Gunakan pembungkus Dynamic ConfigurationManager "Config" sebagai berikut (Panggilan eksplisit ke Perluas () tidak diperlukan):

var serverName = Config.AppSettings.ServerName;
// returns "db01.mycompany.com"

var connectionString = Config.ConnectionStrings.Default;
// returns "server=db01.mycompany.com;uid=uid;pwd=pwd;Initial Catalog=master;"

Contoh Lanjutan 1 (menggunakan AppSettings sebagai sumber default untuk perluasan token)

Di app.config:

<configuration>
    <appSettings>
        <add key="Environment" value="dev"/>
        <add key="Domain" value="mycompany.com"/>
        <add key="UserId" value="uid"/>
        <add key="Password" value="pwd"/>
        <add key="ServerName" value="db01-{Environment}.{Domain}"/>
        <add key="ReportPath" value="\\{ServerName}\SomeFileShare"/>
    </appSettings>
    <connectionStrings>
        <add name="Default" connectionString="server={ServerName};uid={UserId};pwd={Password};Initial Catalog=master;" provider="System.Data.SqlClient" />
    </connectionStrings>
</configuration>

Gunakan metode ekstensi .Expand () pada string yang akan diperluas:

var connectionString = ConfigurationManager.ConnectionStrings["Default"].ConnectionString;
connectionString.Expand() // returns "server=db01-dev.mycompany.com;uid=uid;pwd=pwd;Initial Catalog=master;"
anderly
sumber
4
Saya pikir jawaban ini sangat di bawah nilai !!
Ahmad
Terima kasih Ahmad! Beri tahu saya betapa Anda menyukai Ekspansif.
anderly
Meskipun ini adalah 'resolusi' waktu proses dari pengaturan aplikasi, ini memecahkan masalah saya karena memiliki pasangan nilai kunci yang berulang. Kami telah secara signifikan mengurangi pemeliharaan konfigurasi kami menggunakan ini. Utopia mutlak di sini adalah menjadikan ini sebagai plugin waktu pembuatan untuk bekerja bersama dengan SlowCheetah. Saya akan memberi +1 lagi jika saya bisa. Barang bagus anderly.
Ahmad
Bisakah Anda memberikan contoh singkat tentang bagaimana perpustakaan Anda dapat digunakan untuk mencapai hal ini?
Ryan Gates
Bagi orang lain yang baru saja tersandung pada hal ini, proyek tersebut telah mati selama 6 tahun, sejak 2011 :(
user1003916
4

Saya pikir saya baru saja melihat pertanyaan ini.

Singkatnya, tidak, tidak ada interpolasi variabel dalam konfigurasi aplikasi.

Anda punya dua pilihan

  1. Anda bisa menggulung sendiri untuk mengganti variabel saat runtime
  2. Pada waktu pembuatan, pijat konfigurasi aplikasi ke spesifikasi tertentu dari lingkungan penerapan target. Beberapa detail tentang ini dalam menangani konfigurasi-mimpi buruk
Scott Weinstein
sumber
Ini adalah postingan yang benar. Posting saya sebelumnya (pertanyaan yang sama) tidak menampilkan contoh entri app.config xml. Saya memeriksa tautan Anda - terlalu banyak pekerjaan dan memilih untuk tidak menghabiskan waktu di sana. Kami memiliki app.configs terpisah untuk kotak yang berbeda dan saya ingin melepaskan diri dari itu.
DeeStackOverflow
3

Anda memiliki dua pilihan. Anda dapat melakukan ini dengan langkah build / deploy yang akan memproses file konfigurasi Anda dengan mengganti variabel Anda dengan nilai yang benar.

Pilihan lain adalah menentukan bagian Konfigurasi Anda sendiri yang mendukung ini. Misalnya bayangkan xml ini:

<variableAppSettings>
 <variables>
    <add key="@BaseDir" value="c:\Programs\Widget"/>
 </variables>
 <appSettings>
    <add key="PathToDir" value="@BaseDir\Dir1"/>
 </appSettings>
</variableAppSettings>

Sekarang Anda akan menerapkan ini menggunakan objek konfigurasi khusus yang akan menangani penggantian variabel untuk Anda pada waktu proses.

JoshBerke
sumber
Saya tidak melihat xml Anda di pos (masukkan karakter baris 5 Anda agar dapat mengirim tag xml - saya mengalami masalah yang sama terakhir kali). Selain itu, apa itu 'objek konfigurasi khusus'? Saya lebih suka zero coding untuk mencapai ini karena perubahan coding pada tahap ini akan membuat kami mundur.
DeeStackOverflow
Konfigurasi kustom pasti melibatkan pengkodean [sederhana]. Tapi IMHO selalu menjadi pilihan terbaik Anda. Saya hampir tidak pernah menggunakan appSettings, lebih memilih untuk membuat konfigurasi khusus untuk setiap proyek.
Portman
3

Biasanya, saya akhirnya menulis kelas statis dengan properti untuk mengakses setiap pengaturan web.config saya.

public static class ConfigManager 
{
    public static string MyBaseDir
    {
        return ConfigurationManager.AppSettings["MyBaseDir"].toString();
    }

    public static string Dir1
    {
        return MyBaseDir + ConfigurationManager.AppSettings["Dir1"].toString();
    }

}

Biasanya, saya juga melakukan konversi tipe saat diminta di kelas ini. Ini memungkinkan untuk memiliki akses yang diketik ke konfigurasi Anda, dan jika pengaturan berubah, Anda dapat mengeditnya hanya di satu tempat.

Biasanya, mengganti pengaturan dengan kelas ini relatif mudah dan memberikan kemudahan pemeliharaan yang jauh lebih besar.

Martin
sumber
3

Anda dapat menggunakan variabel lingkungan dalam app.configskenario yang Anda gambarkan

<configuration>
  <appSettings>
    <add key="Dir1" value="%MyBaseDir%\Dir1"/>
  </appSettings>
</configuration>

Kemudian Anda bisa dengan mudah mendapatkan jalur dengan:

var pathFromConfig = ConfigurationManager.AppSettings["Dir1"];
var expandedPath = Environment.ExpandEnvironmentVariables(pathFromConfig);
autocro
sumber
2

Di dalamnya <appSettings>Anda dapat membuat kunci aplikasi,

<add key="KeyName" value="Keyvalue"/>

Nanti Anda dapat mengakses nilai-nilai ini menggunakan:

ConfigurationManager.AppSettings["Keyname"]
Sergio
sumber
Untuk menggunakan kelas ConfigurationManager Anda perlu menambahkan referensi ke System.Configuration dan menambahkan pernyataan menggunakan untuk System.Configuration (impor dalam VB)
cjk
2
Indikasinya benar tetapi bukan jawaban atas pertanyaan yang diajukan.
Michaël Carpentier
1

Saya sarankan Anda DslConfig . Dengan DslConfig Anda dapat menggunakan file konfigurasi hierarki dari Global Config, Config per host server untuk mengkonfigurasi per aplikasi pada setiap host server (lihat AppSpike).
Jika ini menjadi rumit bagi Anda, Anda cukup menggunakan konfigurasi global Variables.var.
Cukup konfigurasikan di Varibales.var

baseDir = "C:\MyBase"
Var["MyBaseDir"] = baseDir
Var["Dir1"] = baseDir + "\Dir1"
Var["Dir2"] = baseDir + "\Dir2"

Dan dapatkan nilai konfigurasi dengan

Configuration config = new DslConfig.BooDslConfiguration()
config.GetVariable<string>("MyBaseDir")
config.GetVariable<string>("Dir1")
config.GetVariable<string>("Dir2")
Johannes
sumber
0

Saya rasa Anda tidak dapat mendeklarasikan dan menggunakan variabel untuk menentukan kunci appSettings dalam file konfigurasi. Saya selalu mengelola penggabungan dalam kode seperti Anda.

Michaël Carpentier
sumber
0

Saya sedikit kesulitan dengan apa yang Anda inginkan, tetapi Anda dapat menambahkan file override ke pengaturan aplikasi kemudian mengatur file override tersebut berdasarkan lingkungan.

<appSettings file="..\OverrideSettings.config">
Andrew Barrett
sumber
0

Untuk meluncurkan produk di mana kami perlu mengonfigurasi banyak item dengan nilai yang serupa, kami menggunakan aplikasi konsol kecil yang membaca XML dan memperbarui berdasarkan parameter yang diteruskan. Ini kemudian dipanggil oleh penginstal setelah meminta pengguna untuk informasi yang dibutuhkan.

cjk
sumber
0

Saya akan merekomendasikan mengikuti solusi Matt Hamsmith. Jika ini adalah masalah untuk diterapkan, mengapa tidak membuat metode ekstensi yang mengimplementasikannya di latar belakang kelas AppSettings?

Sesuatu seperti:

    public static string GetValue(this NameValueCollection settings, string key)
    {

    }

Di dalam metode Anda mencari melalui DictionaryInfoConfigSection menggunakan Linq dan mengembalikan nilai dengan kunci yang cocok. Anda harus memperbarui file konfigurasi, ke sesuatu di sepanjang baris ini:

<appSettings>
  <DirectoryMappings>
    <DirectoryMap key="MyBaseDir" value="C:\MyBase" />
    <DirectoryMap key="Dir1" value="[MyBaseDir]\Dir1"/>
    <DirectoryMap key="Dir2" value="[MyBaseDir]\Dir2"/>
  </DirectoryMappings>
</appSettings>
Kaya
sumber
0

Saya datang dengan solusi ini:

  1. Di aplikasi Settings.settings saya mendefinisikan variabel ConfigurationBase (dengan type = string Scope = Application)
  2. Saya memperkenalkan variabel di atribut target di Settings.settings, semua atribut itu harus diatur ke Scope = User
  3. Di app.xaml.cs saya membaca nilai jika ConfigurationBase
  4. Di app.xaml.cs saya mengganti semua variabel dengan nilai ConfigurationBase. Untuk mengganti nilai pada saat run-time, atribut harus disetel ke Scopr = Pengguna

Saya tidak terlalu senang dengan solusi ini karena saya harus mengubah semua atribut secara manual, jika saya menambahkan yang baru, saya harus mempertimbangkannya di app.xaml.cs.

Berikut cuplikan kode dari App.xaml.cs:

string configBase = Settings.Default.ConfigurationBase;
Settings.Default.CommonOutput_Directory = Settings.Default.CommonOutput_Directory.Replace("${ConfigurationBase}", configBase);

MEMPERBARUI

Baru saja menemukan peningkatan (sekali lagi cuplikan kode dari app.xaml.cs):

string configBase = Settings.Default.ConfigurationBase;

foreach (SettingsProperty settingsProperty in Settings.Default.Properties)
{
    if (!settingsProperty.IsReadOnly && settings.Default[settingsProperty.Name] is string)
    {
        Settings.Default[settingsProperty.Name] = ((string)Settings.Default[settingsProperty.Name]).Replace("${ConfigurationBase}", configBase);
    }
}

Sekarang penggantian berfungsi untuk semua atribut dalam pengaturan saya yang memiliki Type = string dan Scope = User. Saya rasa saya suka seperti ini.

UPDATE2

Rupanya pengaturan Scope = Aplikasi tidak diperlukan saat menjalankan properti.

anhoppe
sumber
0

Tiga Solusi yang Mungkin

Saya tahu saya datang terlambat ke pesta, saya telah mencari apakah ada solusi baru untuk masalah pengaturan konfigurasi variabel. Ada beberapa jawaban yang menyentuh solusi yang telah saya gunakan di masa lalu, tetapi sebagian besar tampak agak berbelit-belit. Saya pikir saya akan melihat solusi lama saya dan menggabungkan implementasinya sehingga dapat membantu orang yang berjuang dengan masalah yang sama.

Untuk contoh ini saya telah menggunakan pengaturan aplikasi berikut di aplikasi konsol:

<appSettings>
    <add key="EnvironmentVariableExample" value="%BaseDir%\bin"/>
    <add key="StaticClassExample" value="bin"/>
    <add key="InterpollationExample" value="{0}bin"/>
  </appSettings>

1. Gunakan variabel lingkungan

Saya percaya jawaban autocro autocro menyentuh itu. Saya hanya melakukan implementasi yang seharusnya cukup saat membangun atau men-debug tanpa harus menutup studio visual. Saya telah menggunakan solusi ini dulu ...

  • Buat acara pra-bangun yang akan menggunakan variabel MSBuild

    Peringatan: Gunakan variabel yang tidak akan mudah diganti jadi gunakan nama proyek Anda atau sesuatu yang serupa sebagai nama variabel.

    SETX BaseDir "$(ProjectDir)"

  • Setel ulang variabel; menggunakan sesuatu seperti berikut:

    Segarkan Variabel Lingkungan di Stack Overflow

  • Gunakan pengaturan di kode Anda:

'

private void Test_Environment_Variables()
{
    string BaseDir = ConfigurationManager.AppSettings["EnvironmentVariableExample"];
    string ExpandedPath = Environment.ExpandEnvironmentVariables(BaseDir).Replace("\"", ""); //The function addes a " at the end of the variable
    Console.WriteLine($"From within the C# Console Application {ExpandedPath}");
}

'

2. Gunakan interpolasi string:

  • Gunakan fungsi string.Format ()

`

private void Test_Interpollation()
{
    string ConfigPath = ConfigurationManager.AppSettings["InterpollationExample"];
    string SolutionPath = Path.GetFullPath(Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, @"..\..\"));
    string ExpandedPath = string.Format(ConfigPath, SolutionPath.ToString());
    Console.WriteLine($"Using old interpollation {ExpandedPath}");
}

`

3. Menggunakan kelas statis, Ini adalah solusi yang paling sering saya gunakan.

  • Pelaksanaan

`

private void Test_Static_Class()
{
    Console.WriteLine($"Using a static config class {Configuration.BinPath}");
}

`

  • Kelas statis

`

static class Configuration
{
    public static string BinPath
    {
        get
        {
            string ConfigPath = ConfigurationManager.AppSettings["StaticClassExample"];
            string SolutionPath = Path.GetFullPath(Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, @"..\..\"));
            return SolutionPath + ConfigPath;
        }
    }
}

`

Kode proyek:

App.config:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <startup> 
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" />
    </startup>
  <appSettings>
    <add key="EnvironmentVariableExample" value="%BaseDir%\bin"/>
    <add key="StaticClassExample" value="bin"/>
    <add key="InterpollationExample" value="{0}bin"/>
  </appSettings>
</configuration>

Program.cs

using System;
using System.Configuration;
using System.IO;

namespace ConfigInterpollation
{
    class Program
    {
        static void Main(string[] args)
        {
            new Console_Tests().Run_Tests();
            Console.WriteLine("Press enter to exit");
            Console.ReadLine();
        }        
    }

    internal class Console_Tests
    {
        public void Run_Tests()
        {
            Test_Environment_Variables();
            Test_Interpollation();
            Test_Static_Class();
        }
        private void Test_Environment_Variables()
        {
            string ConfigPath = ConfigurationManager.AppSettings["EnvironmentVariableExample"];
            string ExpandedPath = Environment.ExpandEnvironmentVariables(ConfigPath).Replace("\"", "");
            Console.WriteLine($"Using environment variables {ExpandedPath}");
        }

        private void Test_Interpollation()
        {
            string ConfigPath = ConfigurationManager.AppSettings["InterpollationExample"];
            string SolutionPath = Path.GetFullPath(Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, @"..\..\"));
            string ExpandedPath = string.Format(ConfigPath, SolutionPath.ToString());
            Console.WriteLine($"Using interpollation {ExpandedPath}");
        }

        private void Test_Static_Class()
        {
            Console.WriteLine($"Using a static config class {Configuration.BinPath}");
        }
    }

    static class Configuration
    {
        public static string BinPath
        {
            get
            {
                string ConfigPath = ConfigurationManager.AppSettings["StaticClassExample"];
                string SolutionPath = Path.GetFullPath(Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, @"..\..\"));
                return SolutionPath + ConfigPath;
            }
        }
    }
}

Acara pra-bangun:

Pengaturan Proyek -> Acara Bangun

StormChild
sumber