Змінні в app.config / web.config


92

Чи можна зробити щось на зразок наступного у файлах app.configor web.config?

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

Потім я хочу отримати доступ до Dir2 у своєму коді, просто сказавши:

 ConfigurationManager.AppSettings["Dir2"]

Це допоможе мені, коли я встановлюю свою програму на різні сервери та місця, де мені доведеться змінити лише ОДИН запис у своєму цілому app.config. (Я знаю, що можу управляти всім об’єднанням у коді, але я віддаю перевагу цьому).


Я думаю, він говорить про визначення змінних для використання в клавішах appSettings безпосередньо у файлах конфігурації.
— Міхаел Карпентьє

1
Я також перевірив використання декларації XML <! ENTITY>, але вона не підтримується через те, що MS обробляє файли web.config.
— chilltemp 02.03.09

Дякую за ваші зусилля. Я вважаю за краще не змінювати будь-який код. У коді вже є висловлювання: string dir2 = ConfigurationManager.AppSettings ["Dir2"]. Я хочу лише очистити app.config, який тепер говорить value = "D: \ blahdir \ Dir2" замість value = "[MyBaseDir] \ Dir2"
— DeeStackOverflow

Відповіді:


7

Гарне питання.

Я не думаю, що існує. Я вважаю, що це було б досить добре відомо, якби був простий спосіб, і я бачу, що Microsoft створює механізм у Visual Studio 2010 для розгортання різних файлів конфігурації для розгортання та тестування.

Однак з урахуванням сказаного; Я виявив, що у вас у цьому ConnectionStringsрозділі є своєрідний заповнювач, який називається "| DataDirectory |". Можливо, ви могли б поглянути, що там працює ...

Ось фрагмент із machine.configйого показу:

 <connectionStrings>
    <add
        name="LocalSqlServer"
        connectionString="data source=.\SQLEXPRESS;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|aspnetdb.mdf;User Instance=true"
        providerName="System.Data.SqlClient"
    />
 </connectionStrings>

Це цікава інформація. Можливо, доступ до змінних здійснюється за допомогою символу каналу ("|")? Хм .. Мені цікаво, чи це спрацює: <add key = "Dir2" value = "| MyBaseDir | \ Dir2" />
— DeeStackOverflow

4
Значення DataDirectory насправді є елементом даних в AppDomain. Ви можете замінити значення за допомогою AppDomain.CurrentDomain.SetData ("DataDirectory", dataPath); Я не тестував, чи можете ви визначити інші змінні, подібні до цього, і отримати їх "авторозгорнутим", хоча ...
— Пітер Ліллевольд,

22

Трохи більш складною, але набагато гнучкішою альтернативою є створення класу, який представляє розділ конфігурації. У вашому app.config/ web.configфайлі ви можете мати таке:

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

Потім у коді .NET (я буду використовувати C # у своєму прикладі) ви можете створити два класи, як це:

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

Нарешті, у коді програми ви можете отримати доступ до своїх app.configзмінних, використовуючи нові класи, таким чином:

DirectoryInfoConfigSection config =
  (DirectoryInfoConfigSection)ConfigurationManager.GetSection("DirectoryInfo");
String dir1Path = config.Directory.Directory1Resolved;  // This value will equal "C:\MyBase\Dir1"

1
Дякую, але я намагаюся зробити це, не змінюючи жодного коду, оскільки на цьому етапі це боляче.
— DeeStackOverflow

В останньому рядку коду (не враховуючи фігурні дужки) є невелика помилка: "return System.IO.Path.Combine (MyBaseDir, Dir1);" замість цього має бути "return System.IO.Path.Combine (BaseDirectory, Dir1);", або інакше метод слід перейменовувати з "Базового каталогу" у "MyBaseDir"
— TheWho

16

Ви можете досягти за допомогою моєї бібліотеки Expansive . Також доступний на Nuget тут .

Він був розроблений з цим як основний варіант використання.

Помірний приклад (використання AppSettings як джерела за замовчуванням для розширення маркера)

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

Використовуйте метод розширення .Expand () для рядка, який потрібно розширити:

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

або

Використовуйте обгортку Dynamic ConfigurationManager "Config" наступним чином (явний виклик Expand () не потрібен):

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

Розширений приклад 1 (використання AppSettings як джерела за замовчуванням для розширення маркера)

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

Використовуйте метод розширення .Expand () для рядка, який потрібно розширити:

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

4
Я думаю, що ця відповідь дуже занижена !!
— Ахмад

Дякую Ахмаде! Повідомте мене, як вам подобається Expansive.
— anderly 01.03.13

Незважаючи на те, що це `` роздільна здатність '' налаштувань програми, вона вирішує мої проблеми з повторюваними парами значень ключа. Ми значно скоротили обслуговування конфігурації, використовуючи це. Абсолютною утопією тут було б, щоб цей плагін часу збірки працював спільно з SlowCheetah. Я б ще раз +1, якби міг. Чудові речі anderly.
— Ахмад

Чи можете ви навести короткий приклад того, як ваша бібліотека може бути використана для цього?
— Райан Гейтс,

Для тих, хто тільки зараз натрапляє на це, проект уже мертвий вже 6 років, починаючи з 2011 року :(
— user1003916

4

Мені здалося, я щойно побачив це запитання.

Коротше кажучи, ні, в конфігурації програми немає змінної інтерполяції.

У вас є два варіанти

  1. Ви можете прокрутити власні, щоб замінити змінні під час виконання
  2. Під час побудови додавайте конфігурацію програми конкретним особливостям цільового середовища розгортання. Деякі подробиці з цього при роботі з конфігурацією-кошмаром

Це правильний пост. У моєму попередньому дописі (те саме запитання) не було показано прикладу входу в app.config xml. Я перевірив ваше посилання - це занадто багато роботи і вважаю за краще не проводити там час. У нас є окремі app.configs для різних вікон, і я хочу піти від цього.
— DeeStackOverflow 02.03.09

3

У вас є кілька варіантів. Ви можете зробити це за допомогою кроку збірки / розгортання, який обробить ваш файл конфігурації, замінивши ваші змінні правильним значенням.

Іншим варіантом було б визначити свій власний розділ конфігурації, який це підтримував. Наприклад, уявіть цей xml:

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

Тепер ви б реалізували це, використовуючи спеціальні об'єкти конфігурації, які могли б обробляти заміну змінних для вас під час виконання.


Я не бачу ваш xml у дописі (зробіть відступ у рядку 5 символів, щоб мати можливість розміщувати теги xml - минулого разу у мене була та сама проблема). Крім того, що таке "спеціальні об'єкти конфігурації"? Я віддаю перевагу нульовому кодуванню, щоб досягти цього, оскільки зміни кодування на цьому етапі значно нас повернуть.
— DeeStackOverflow

Спеціальна конфігурація, безумовно, передбачає [просте] кодування. Але IMHO - це завжди ваш найкращий варіант. Я майже ніколи не використовую appSettings, вважаючи за краще створювати власні налаштування для кожного проекту.
— Портман

3

Зазвичай я в кінцевому підсумку пишу статичний клас із властивостями для доступу до кожного з налаштувань мого web.config.

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

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

}

Зазвичай я також виконую перетворення типу, коли це потрібно в цьому класі. Це дозволяє мати набраний доступ до вашої конфігурації, і якщо налаштування змінюються, ви можете редагувати їх лише в одному місці.

Зазвичай замінити налаштування на цей клас відносно просто і забезпечує набагато більшу ремонтопридатність.


3

Ви можете використовувати змінні середовища у своєму app.configсценарії, який ви описуєте

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

Тоді ви можете легко отримати шлях за допомогою:

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

2

Усередині <appSettings>ви можете створювати ключі програм,

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

Пізніше ви зможете отримати доступ до цих значень за допомогою:

ConfigurationManager.AppSettings["Keyname"]

Для використання класу ConfigurationManager потрібно додати посилання на System.Configuration та додати оператор using для System.Configuration (імпорт у VB)
— cjk

2
Вказівка ​​є правильною, але не є відповіддю на поставлене питання.
— Міхаел Карпентьє

1

Я б запропонував вам DslConfig . За допомогою DslConfig ви можете використовувати ієрархічні файли конфігурації з Global Config, Config на хост сервера, щоб налаштувати кожну програму на кожному сервері (див. AppSpike).
Якщо для вас це ускладнено, ви можете просто використати глобальну конфігурацію Variables.var
Просто налаштуйте у Varibales.var

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

І отримайте значення конфігурації за допомогою

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

0

Я не думаю, що ви можете оголосити та використовувати змінні для визначення ключів appSettings у файлі конфігурації. Я завжди керував об’єднаннями в коді, як ви.


0

Я трохи борюся з тим, що ви хочете, але ви можете додати файл перевизначення до налаштувань програми, а потім встановити цей файл перевизначення для кожного середовища.

<appSettings file="..\OverrideSettings.config">

0

Для розгортання продуктів, де нам потрібно налаштувати велику кількість елементів із подібними значеннями, ми використовуємо невеликі консольні програми, які читають XML і оновлюються на основі переданих параметрів. Вони потім викликаються програмою встановлення після того, як він запитає у користувача про Необхідна інформація.


0

Я б рекомендував дотримуватися рішення Метта Гамсміта. Якщо це проблема для реалізації, то чому б не створити метод розширення, який реалізує це у фоновому режимі в класі AppSettings?

Щось на зразок:

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

    }

Усередині методу ви здійснюєте пошук за допомогою DictionaryInfoConfigSection за допомогою Linq і повертаєте значення за допомогою відповідного ключа. Вам потрібно буде оновити конфігураційний файл, щоб зробити щось подібне:

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

0

Я придумав таке рішення:

  1. У програмі Settings.settings я визначив змінну ConfigurationBase (з type = string Scope = Application)
  2. Я ввів змінну в цільові атрибути в Settings.settings, для всіх цих атрибутів потрібно було встановити Scope = User
  3. У app.xaml.cs я прочитав значення, якщо ConfigurationBase
  4. У app.xaml.cs я замінив усі змінні значенням ConfigurationBase. Для заміни значень під час виконання атрибути повинні були бути встановлені на Scopr = User

Я не дуже задоволений цим рішенням, тому що мені доводиться змінювати всі атрибути вручну, якщо я додаю новий, я повинен враховувати його в app.xaml.cs.

Ось фрагмент коду з App.xaml.cs:

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

ОНОВЛЕННЯ

Щойно знайшов поліпшення (знову фрагмент коду з 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);
    }
}

Тепер заміни працюють для всіх атрибутів у моїх налаштуваннях, які мають Type = string та Scope = User. Думаю, мені подобається так.

ОНОВЛЕННЯ2

Очевидно, встановлення Scope = Застосування не потрібне при запуску властивостей.


0

Три можливі рішення

Я знаю, що пізно заходжу на вечірку, я шукав, чи є якісь нові рішення проблеми з налаштуваннями змінної конфігурації. Є кілька відповідей, які стосуються рішень, які я використовував у минулому, але більшість здаються дещо заплутаними. Я думав, що я подивлюсь на свої старі рішення та складу їх, щоб це могло допомогти людям, які борються з тією ж проблемою.

У цьому прикладі я використав такий параметр програми в консольному додатку:

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

1. Використовуйте змінні середовища

Я вважаю , що відповідь autocro autocro торкнулася її. Я просто роблю реалізацію, якої має бути достатньо під час створення або налагодження без необхідності закривати візуальну студію. Я використовував це рішення ще в той день ...

  • Створіть подію попередньої збірки, яка використовуватиме змінні MSBuild

    Попередження: Використовуйте змінну, яка не буде легко замінена, тому використовуйте назву проекту або щось подібне як назву змінної.

    SETX BaseDir "$(ProjectDir)"

  • Скинути змінні; використовуючи щось на зразок наступного:

    Оновити змінні середовища при переповненні стека

  • Використовуйте налаштування у своєму коді:

'

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. Використовуйте інтерполяцію рядків:

  • Використовуйте функцію 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. Використовуючи статичний клас, це рішення я переважно використовую.

  • Впровадження

`

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

`

Код проекту:

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

Подія перед збіркою:

Налаштування проекту -> Створення подій

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.