Variáveis ​​em app.config / web.config

92

É possível fazer algo como o seguinte nos arquivos app.configou web.config?

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

Desejo acessar Dir2 em meu código simplesmente dizendo:

 ConfigurationManager.AppSettings["Dir2"]

Isso me ajudará quando eu instalar meu aplicativo em diferentes servidores e locais onde terei que alterar apenas UMA entrada em toda a minha app.config. (Sei que posso gerenciar toda a concatenação no código, mas prefiro assim).

DeeStackOverflow
fonte
Acho que ele está falando sobre definir variáveis ​​para usar nas chaves appSettings diretamente nos arquivos de configuração.
Michaël Carpentier,
1
Também fiz check-out usando a declaração XML <! ENTITY>, mas não há suporte devido à forma como o MS lida com arquivos web.config.
chilltemp
Obrigado por seus esforços. Prefiro não modificar nenhum código. O código já tem uma declaração dizendo: string dir2 = ConfigurationManager.AppSettings ["Dir2"]. Eu só quero limpar o app.config que agora diz value = "D: \ blahdir \ Dir2" em vez de value = "[MyBaseDir] \ Dir2"
DeeStackOverflow

Respostas:

7

Boa pergunta.

Acho que não. Acredito que seria bastante conhecido se houvesse uma maneira fácil, e vejo que a Microsoft está criando um mecanismo no Visual Studio 2010 para implantar diferentes arquivos de configuração para implantação e teste.

Com isso dito, no entanto; Eu descobri que você na ConnectionStringsseção tem um tipo de espaço reservado chamado "| DataDirectory |". Talvez você possa dar uma olhada no que está acontecendo lá ...

Aqui está um pedaço de como machine.configmostrá-lo:

 <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
fonte
Essa é uma informação interessante. Talvez as variáveis ​​sejam acessadas usando o símbolo de barra vertical ("|")? Hmm .. Eu me pergunto se isso vai funcionar: <add key = "Dir2" value = "| MyBaseDir | \ Dir2" />
DeeStackOverflow
4
O valor DataDirectory é na verdade um elemento de dados no AppDomain. Você pode substituir o valor usando AppDomain.CurrentDomain.SetData ("DataDirectory", dataPath); Eu não testei se você pode definir outras variáveis ​​como esta e obtê-las "autoexpandidas" embora ...
Peter Lillevold
22

Uma alternativa um pouco mais complicada, mas muito mais flexível, é criar uma classe que represente uma seção de configuração. Em seu arquivo app.config/ web.config, você pode ter isto:

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

Então, em seu código .NET (usarei C # em meu exemplo), você pode criar duas classes como esta:

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

Finalmente, em seu código de programa, você pode acessar suas app.configvariáveis, usando suas novas classes, desta maneira:

DirectoryInfoConfigSection config =
  (DirectoryInfoConfigSection)ConfigurationManager.GetSection("DirectoryInfo");
String dir1Path = config.Directory.Directory1Resolved;  // This value will equal "C:\MyBase\Dir1"
Matt Hamsmith
fonte
1
Obrigado, mas estou tentando fazer isso sem modificar nenhum código, pois é uma dor neste estágio.
DeeStackOverflow
Há um pequeno erro na última linha do código (sem contar as chaves): "return System.IO.Path.Combine (MyBaseDir, Dir1);" deve ser "return System.IO.Path.Combine (BaseDirectory, Dir1);", ou então o método deve ser renomeado de 'Base Directory' para 'MyBaseDir'
TheWho
16

Você pode realizar usando minha biblioteca Expansive . Também disponível no nuget aqui .

Ele foi projetado com isso como um caso de uso principal.

Exemplo moderado (usando AppSettings como fonte padrão para expansão de token)

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

Use o método de extensão .Expand () na string a ser expandida:

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

ou

Use o wrapper Dynamic ConfigurationManager "Config" da seguinte forma (chamada explícita para Expand () não é necessária):

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

Exemplo avançado 1 (usando AppSettings como fonte padrão para expansão de token)

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

Use o método de extensão .Expand () na string a ser expandida:

var connectionString = ConfigurationManager.ConnectionStrings["Default"].ConnectionString;
connectionString.Expand() // returns "server=db01-dev.mycompany.com;uid=uid;pwd=pwd;Initial Catalog=master;"
Anderly
fonte
4
Acho que essa resposta está muito abaixo da média !!
Ahmad
Obrigado Ahmad! Deixe-me saber se você gosta do Expansive.
apenas
Embora esta seja a 'resolução' das configurações do aplicativo em tempo de execução, ela resolve meus problemas de ter pares de valores-chave repetitivos. Reduzimos significativamente nossa manutenção de configuração usando isso. A utopia absoluta aqui seria ter este sendo um plugin de tempo de construção para trabalhar em conjunto com o SlowCheetah. Eu marcaria de novo se pudesse. Coisas boas e apenas.
Ahmad
Você pode fornecer um breve exemplo de como sua biblioteca pode ser usada para fazer isso?
Ryan Gates
Para qualquer pessoa que acabe de se deparar com isso, o projeto está morto há 6 anos, desde 2011 :(
user1003916
4

Eu pensei ter acabado de ver esta pergunta.

Resumindo, não, não há interpolação de variáveis ​​dentro de uma configuração de aplicativo.

Você tem duas opções

  1. Você pode lançar o seu próprio para substituir variáveis ​​em tempo de execução
  2. No momento da construção, adapte a configuração do aplicativo às especificidades particulares do ambiente de implementação de destino. Alguns detalhes sobre como lidar com o pesadelo da configuração
Scott Weinstein
fonte
Esta é a postagem correta. Meu post anterior (mesma pergunta) não mostrou o exemplo de entrada app.config xml. Eu verifiquei seu link - é muito trabalhoso e prefiro não perder tempo lá. Temos app.configs separados para caixas diferentes e quero ficar longe disso.
DeeStackOverflow
3

Você tem algumas opções. Você poderia fazer isso com uma etapa de construção / implementação que processaria seu arquivo de configuração, substituindo suas variáveis ​​pelo valor correto.

Outra opção seria definir sua própria seção de configuração que suportasse isso. Por exemplo, imagine este xml:

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

Agora você implementaria isso usando objetos de configuração personalizados que tratariam da substituição das variáveis ​​para você no tempo de execução.

JoshBerke
fonte
Não vejo seu xml no post (indente sua linha 5 caracteres para poder postar tags xml - tive o mesmo problema da última vez). Além disso, o que são 'objetos de configuração personalizados'? Eu prefiro a codificação zero para conseguir isso, pois as mudanças de codificação neste estágio nos atrasariam muito.
DeeStackOverflow
A configuração personalizada definitivamente envolve codificação [simples]. Mas IMHO é sempre sua melhor opção. Quase nunca uso appSettings, preferindo, em vez disso, criar uma configuração personalizada para cada projeto.
Portman
3

Normalmente, acabo escrevendo uma classe estática com propriedades para acessar cada uma das configurações do meu 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();
    }

}

Normalmente, também faço conversões de tipo quando necessário nesta aula. Ele permite ter um acesso digitado à sua configuração e, se as configurações forem alteradas, você poderá editá-las em apenas um lugar.

Normalmente, substituir as configurações por esta classe é relativamente fácil e fornece uma capacidade de manutenção muito maior.

Martin
fonte
3

Você pode usar variáveis ​​de ambiente em seu app.configpara esse cenário que você descreve

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

Então você pode facilmente obter o caminho com:

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

Dentro, <appSettings>você pode criar chaves de aplicativo,

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

Mais tarde, você pode acessar esses valores usando:

ConfigurationManager.AppSettings["Keyname"]
Sergio
fonte
Para usar a classe ConfigurationManager, você precisa adicionar uma referência a System.Configuration e adicionar uma instrução using para System.Configuration (importações em VB)
cjk
2
A indicação está correta, mas não é uma resposta à pergunta feita.
Michaël Carpentier
1

Eu sugiro que você DslConfig . Com DslConfig, você pode usar arquivos de configuração hierárquicos de Global Config, Config por host de servidor para configurar por aplicativo em cada host de servidor (consulte o AppSpike).
Se isso for muito complicado para você, você pode apenas usar a configuração global Variables.var
Apenas configure em Varibales.var

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

E obter os valores de configuração com

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

Não acho que você possa declarar e usar variáveis ​​para definir as chaves appSettings em um arquivo de configuração. Sempre administrei concatenações em código como você.

Michaël Carpentier
fonte
0

Estou lutando um pouco com o que você deseja, mas você pode adicionar um arquivo de substituição às configurações do aplicativo e ter esse arquivo de substituição definido para cada ambiente.

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

Para lançar produtos em que precisamos configurar muitos itens com valores semelhantes, usamos pequenos aplicativos de console que leem o XML e atualizam com base nos parâmetros passados. Eles são chamados pelo instalador depois de solicitar ao usuário o informação requerida.

cjk
fonte
0

Eu recomendaria seguir a solução de Matt Hamsmith. Se for um problema de implementação, por que não criar um método de extensão que implemente isso em segundo plano na classe AppSettings?

Algo como:

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

    }

Dentro do método, você pesquisa em DictionaryInfoConfigSection usando Linq e retorna o valor com a chave correspondente. No entanto, você precisará atualizar o arquivo de configuração para algo assim:

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

Eu descobri esta solução:

  1. No aplicativo Settings.settings eu defini uma variável ConfigurationBase (com type = string Scope = Application)
  2. Eu introduzi uma variável nos atributos de destino em Settings.settings, todos esses atributos tiveram que ser definidos como Scope = User
  3. No app.xaml.cs eu li o valor se o ConfigurationBase
  4. No app.xaml.cs, substituí todas as variáveis ​​pelo valor ConfigurationBase. A fim de substituir os valores em tempo de execução, os atributos tiveram que ser definidos como Scopr = User

Não estou muito feliz com esta solução porque tenho que alterar todos os atributos manualmente, se eu adicionar um novo, tenho que considerá-lo no app.xaml.cs.

Aqui está um snippet de código do App.xaml.cs:

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

ATUALIZAR

Acabei de encontrar uma melhoria (novamente um trecho de código do 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);
    }
}

Agora as substituições funcionam para todos os atributos em minhas configurações que têm Type = string e Scope = User. Eu acho que gosto desse jeito.

ATUALIZAÇÃO2

Aparentemente, definir Scope = Application não é necessário ao executar as propriedades.

Anhoppe
fonte
0

Três Soluções Possíveis

Sei que estou chegando atrasado para a festa, estive procurando se havia alguma solução nova para o problema das configurações variáveis. Existem algumas respostas que tocam as soluções que usei no passado, mas a maioria parece um pouco complicada. Pensei em olhar minhas soluções antigas e colocar as implementações juntas para que pudessem ajudar as pessoas que estão lutando com o mesmo problema.

Para este exemplo, usei a seguinte configuração de aplicativo em um aplicativo de console:

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

1. Use variáveis ​​de ambiente

Eu acredito que a resposta do autocro autocro tocou nisso. Estou apenas fazendo uma implementação que deve ser suficiente ao compilar ou depurar sem ter que fechar o Visual Studio. Eu usei essa solução há muito tempo ...

  • Crie um evento de pré-construção que usará as variáveis ​​MSBuild

    Aviso: Use uma variável que não será substituída facilmente, então use o nome do seu projeto ou algo semelhante como nome de variável.

    SETX BaseDir "$(ProjectDir)"

  • Redefinir variáveis; usando algo como o seguinte:

    Atualizar variáveis ​​de ambiente no Stack Overflow

  • Use a configuração em seu código:

'

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. Use interpolação de string:

  • Use a função 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. Usando uma classe estática, esta é a solução que mais uso.

  • A implementação

`

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

`

  • A classe estática

`

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

`

Código do projeto:

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

Evento de pré-construção:

Configurações do projeto -> Eventos de construção

StormChild
fonte