Переменные в app.config / web.config

92

Можно ли сделать что-то подобное в файлах app.configили 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. (Я знаю, что могу управлять всей конкатенацией в коде, но я предпочитаю такой способ).

DeeStackOverflow
источник
Я думаю, он говорит об определении переменных для использования в ключах appSettings непосредственно в файлах конфигурации.
Michaël Carpentier,
1
Я также проверял, используя объявление XML <! ENTITY>, но оно не поддерживается из-за того, как MS обрабатывает файлы web.config.
chilltemp 02
Спасибо за ваши старания. Я предпочитаю не изменять какой-либо код. В коде уже есть выражение: 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 02
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 02
В последней строке кода есть небольшая ошибка (не считая фигурных скобок): «return System.IO.Path.Combine (MyBaseDir, Dir1);» вместо этого должен быть «return System.IO.Path.Combine (BaseDirectory, Dir1);», иначе метод должен быть переименован из «Base Directory» в «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
Хотя это «разрешение» настроек приложения во время выполнения, оно решает мои проблемы с повторяющимися парами ключ-значение. Используя это, мы значительно сократили объем обслуживания конфигурации. Абсолютной утопией здесь было бы, если бы этот плагин времени сборки работал вместе с SlowCheetah. Я бы снова +1, если бы мог. Отличный материал раньше.
Ахмад
Не могли бы вы привести краткий пример того, как для этого можно использовать вашу библиотеку?
Райан Гейтс
Для всех, кто только что наткнулся на это, проект мертв уже 6 лет, с 2011 года :(
user1003916
4

Я думал, что только что увидел этот вопрос.

Короче говоря, нет, в конфигурации приложения нет интерполяции переменных.

У вас есть два варианта

  1. Вы можете использовать свои собственные для замены переменных во время выполнения
  2. Во время сборки измените конфигурацию приложения с учетом конкретных особенностей целевой среды развертывания. Некоторые подробности об этом при работе с кошмаром конфигурации
Скотт Вайнштейн
источник
Это правильный пост. В моем предыдущем сообщении (тот же вопрос) не отображался пример записи xml app.config. Я проверил вашу ссылку - это слишком много работы, и я предпочитаю не тратить время на нее. У нас есть отдельные app.configs для разных ящиков, и я хочу избежать этого.
DeeStackOverflow 02
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 02
Специальная конфигурация определенно включает в себя [простое] кодирование. Но ИМХО это всегда лучший вариант. Я почти никогда не использую appSettings, предпочитая вместо этого создавать индивидуальную конфигурацию для каждого проекта.
Portman
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
Указание верное, но не ответ на заданный вопрос.
Michaël Carpentier,
1

Я бы посоветовал вам DslConfig . С помощью DslConfig вы можете использовать иерархические файлы конфигурации из Global Config, Config per server host для настройки каждого приложения на каждом хосте сервера (см. 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 и обновляют в соответствии с переданными параметрами. Затем они вызываются установщиком после того, как он запросит у пользователя Необходимая информация.

cjk
источник
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 = Application не требуется при запуске свойств.

Anhoppe
источник
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;
            }
        }
    }
}

Событие перед сборкой:

Настройки проекта -> События сборки

StormChild
источник