it-roy-ru.com

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

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

80
DeeStackOverflow

Хороший вопрос.

Я не думаю, что есть. Я полагаю, что было бы достаточно хорошо известно, если бы существовал простой способ, и я вижу, что 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>
7
Arjan Einbu

Несколько более сложная, но гораздо более гибкая альтернатива - создать класс, представляющий раздел конфигурации. В вашем файле 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"
22
Matt Hamsmith

Вы можете сделать это с помощью моей библиотеки Expansive: http://nuget.org/List/Packages/Expansive Источник доступен здесь: https://github.com/anderly/Expansive

14
anderly

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

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

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

  1. Вы можете свернуть свои собственные, чтобы заменить переменные во время выполнения
  2. Во время сборки сконфигурируйте конфигурацию приложения в соответствии с конкретными особенностями целевой среды развертывания. Некоторые подробности об этом в о конфигурации-кошмаре
4
Scott Weinstein

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

Другой вариант - определить свой собственный раздел конфигурации, который поддерживает это. Например, представьте этот XML:

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

Теперь вы реализуете это с использованием пользовательских объектов конфигурации, которые будут обрабатывать замену переменных для вас во время выполнения. 

3
JoshBerke

Обычно я в конечном итоге пишу статический класс со свойствами для доступа к каждому из параметров моего 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
Martin

Вы можете использовать переменные среды в вашем app.config для того сценария, который вы описываете

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

Тогда вы можете легко получить путь с помощью:

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

Внутри <appSettings> вы можете создавать ключи приложений,

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

Позже вы можете получить доступ к этим значениям, используя: 

ConfigurationManager.AppSettings["Keyname"]
1
Sergio

Я бы предложил вам 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")
1
Johannes

Я придумал это решение:

  1. В приложении Settings.settings я определил переменную ConfigurationBase (с типом = строка Область = Приложение)
  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 не требуется при работе со свойствами.

0
anhoppe

Я бы порекомендовал следовать решению Мэтта Хэмсмита. Если это проблема для реализации, то почему бы не создать метод расширения, который реализует это в фоновом режиме в классе 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
Rich

Три возможных решения

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

Для этого примера я использовал следующие настройки приложения в консольном приложении:

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

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

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

0
StormChild

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

0
Michaël Carpentier

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

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

Для развертывания продуктов, в которых нам нужно настроить множество элементов с одинаковыми значениями, мы используем небольшие консольные приложения, которые читают XML и обновляют на основе переданных параметров. Затем они вызываются установщиком после того, как он запросил у пользователя Необходимая информация.

0
cjk