Змінні в 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.