Variables dentro de app.config / web.config

92

¿Es posible hacer algo como lo siguiente en los archivos app.configo web.config?

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

Luego quiero acceder a Dir2 en mi código simplemente diciendo:

 ConfigurationManager.AppSettings["Dir2"]

Esto me ayudará cuando instale mi aplicación en diferentes servidores y ubicaciones donde solo tendré que cambiar UNA entrada en mi totalidad app.config. (Sé que puedo administrar toda la concatenación en código, pero lo prefiero de esta manera).

DeeStackOverflow
fuente
Creo que está hablando de definir variables para usar en las claves de appSettings directamente dentro de los archivos de configuración.
Michaël Carpentier
1
También comprobé el uso de la declaración XML <! ENTITY>, pero no es compatible debido a la forma en que MS maneja los archivos web.config.
chilltemp
Gracias por tus esfuerzos. Prefiero no modificar ningún código. El código ya tiene una declaración que dice: string dir2 = ConfigurationManager.AppSettings ["Dir2"]. Solo quiero limpiar la app.config que ahora dice value = "D: \ blahdir \ Dir2" en lugar de value = "[MyBaseDir] \ Dir2"
DeeStackOverflow

Respuestas:

7

Buena pregunta.

No creo que lo haya. Creo que habría sido bastante conocido si hubiera una manera fácil, y veo que Microsoft está creando un mecanismo en Visual Studio 2010 para implementar diferentes archivos de configuración para implementación y prueba.

Dicho esto, sin embargo; He descubierto que en la ConnectionStringssección tiene una especie de marcador de posición llamado "| DataDirectory |". Tal vez podrías echar un vistazo a lo que funciona allí ...

Aquí hay un fragmento de machine.configmostrarlo:

 <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
fuente
Esa es una información interesante. ¿Quizás se accede a las variables usando el símbolo de tubería ("|")? Hmm ... Me pregunto si esto funcionará: <add key = "Dir2" value = "| MyBaseDir | \ Dir2" />
DeeStackOverflow
4
El valor de DataDirectory es en realidad un elemento de datos en AppDomain. Puede anular el valor utilizando AppDomain.CurrentDomain.SetData ("DataDirectory", dataPath); Sin embargo, no he probado si puede definir otras variables como esta y obtenerlas "autoexpandidas" ...
Peter Lillevold
22

Una alternativa un poco más complicada, pero mucho más flexible, es crear una clase que represente una sección de configuración. En su archivo app.config/ web.config, puede tener esto:

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

Luego, en su código .NET (usaré C # en mi ejemplo), puede crear dos clases 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, en el código de su programa, puede acceder a sus app.configvariables, usando sus nuevas clases, de esta manera:

DirectoryInfoConfigSection config =
  (DirectoryInfoConfigSection)ConfigurationManager.GetSection("DirectoryInfo");
String dir1Path = config.Directory.Directory1Resolved;  // This value will equal "C:\MyBase\Dir1"
Matt Hamsmith
fuente
1
Gracias, pero estoy tratando de hacer esto sin modificar ningún código, ya que es una molestia en esta etapa.
DeeStackOverflow
Hay un pequeño error en la última línea de código (sin contar las llaves): "return System.IO.Path.Combine (MyBaseDir, Dir1);" en su lugar, debería ser "return System.IO.Path.Combine (BaseDirectory, Dir1);", o de lo contrario, el método debería cambiarse de nombre de 'Base Directory' a 'MyBaseDir'
TheWho
16

Puedes lograrlo usando mi biblioteca Expansiva . También disponible en nuget aquí .

Fue diseñado con esto como un caso de uso principal.

Ejemplo moderado (usando AppSettings como fuente predeterminada para la expansión de tokens)

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

Utilice el método de extensión .Expand () en la cadena que se expandirá:

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

o

Utilice el contenedor Dynamic ConfigurationManager "Config" de la siguiente manera (la llamada explícita a Expand () no es necesaria):

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

Ejemplo avanzado 1 (usando AppSettings como fuente predeterminada para la expansión de tokens)

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

Utilice el método de extensión .Expand () en la cadena que se expandirá:

var connectionString = ConfigurationManager.ConnectionStrings["Default"].ConnectionString;
connectionString.Expand() // returns "server=db01-dev.mycompany.com;uid=uid;pwd=pwd;Initial Catalog=master;"
anciano
fuente
4
¡Creo que esta respuesta está muy subestimada!
Ahmad
¡Gracias Ahmad! Déjame saber si te gusta Expansive.
anderly
Aunque esta es la 'resolución' en tiempo de ejecución de la configuración de la aplicación, resuelve mis problemas de tener pares de valores clave repetitivos. Hemos reducido significativamente nuestro mantenimiento de configuración usando esto. La utopía absoluta aquí sería que este sea un complemento de tiempo de compilación para que funcione junto con SlowCheetah. Volvería a hacer +1 si pudiera. Grandes cosas anderly.
Ahmad
¿Puede proporcionar un breve ejemplo de cómo se podría utilizar su biblioteca para lograr esto?
Ryan Gates
Para cualquier otra persona que se encuentre con esto, el proyecto ha estado muerto durante 6 años, desde 2011 :(
user1003916
4

Pensé que acababa de ver esta pregunta.

En resumen, no, no hay interpolación de variables dentro de la configuración de una aplicación.

Tienes dos opciones

  1. Puede rodar el suyo para sustituir variables en tiempo de ejecución
  2. En el momento de la compilación, aplique la configuración de la aplicación a las características específicas del entorno de implementación de destino. Algunos detalles sobre esto al lidiar con la configuración-pesadilla
Scott Weinstein
fuente
Esta es la publicación correcta. Mi publicación anterior (la misma pregunta) no mostraba el ejemplo de entrada app.config xml. Revisé tu enlace, es demasiado trabajo y prefiero no pasar tiempo allí. Tenemos configuraciones de aplicaciones separadas para diferentes cajas y quiero alejarme de eso.
DeeStackOverflow
3

Tienes unas cuantas opciones. Puede hacer esto con un paso de compilación / implementación que procesaría su archivo de configuración reemplazando sus variables con el valor correcto.

Otra opción sería definir su propia sección de Configuración que lo admita. Por ejemplo, imagina este xml:

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

Ahora implementaría esto usando objetos de configuración personalizados que manejarían reemplazar las variables por usted en tiempo de ejecución.

JoshBerke
fuente
No veo su xml en la publicación (sangra los caracteres de la línea 5 para poder publicar etiquetas xml; tuve el mismo problema la última vez). Además, ¿qué son los 'objetos de configuración personalizados'? Prefiero la codificación cero para lograr esto, ya que los cambios de codificación en esta etapa nos retrasarían mucho.
DeeStackOverflow
La configuración personalizada definitivamente implica una codificación [simple]. Pero en mi humilde opinión, siempre es tu mejor opción. Casi nunca uso appSettings, prefiero crear una configuración personalizada para cada proyecto.
Portman
3

Por lo general, termino escribiendo una clase estática con propiedades para acceder a cada una de las configuraciones de mi 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();
    }

}

Por lo general, también hago conversiones de tipos cuando es necesario en esta clase. Permite tener un acceso escrito a su configuración, y si la configuración cambia, puede editarla en un solo lugar.

Por lo general, reemplazar configuraciones con esta clase es relativamente fácil y proporciona una capacidad de mantenimiento mucho mayor.

Martín
fuente
3

Puede usar variables de entorno en su app.configpara ese escenario que describe

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

Entonces puede obtener fácilmente el camino con:

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

Dentro <appSettings>puede crear claves de aplicación,

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

Más adelante puede acceder a estos valores usando:

ConfigurationManager.AppSettings["Keyname"]
Sergio
fuente
Para usar la clase ConfigurationManager, debe agregar una referencia a System.Configuration y agregar una declaración de uso para System.Configuration (importaciones en VB)
cjk
2
La indicación es correcta pero no es una respuesta a la pregunta formulada.
Michaël Carpentier
1

Te sugiero DslConfig . Con DslConfig puede usar archivos de configuración jerárquica desde Configuración global, Configuración por host de servidor a configuración por aplicación en cada host de servidor (consulte AppSpike).
Si esto es
demasiado complicado para usted, puede usar la configuración global Variables.var Simplemente configure en Varibales.var

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

Y obtén los valores de configuración con

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

No creo que pueda declarar y usar variables para definir las claves de appSettings dentro de un archivo de configuración. Siempre he manejado concatenaciones en código como tú.

Michaël Carpentier
fuente
0

Estoy luchando un poco con lo que quieres, pero puedes agregar un archivo de anulación a la configuración de la aplicación y luego configurar ese archivo de anulación por entorno.

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

Para implementar productos en los que necesitamos configurar una gran cantidad de elementos con valores similares, utilizamos pequeñas aplicaciones de consola que leen el XML y se actualizan en función de los parámetros pasados. A continuación, el instalador las invoca después de haber pedido al usuario el información requerida.

cjk
fuente
0

Recomendaría seguir la solución de Matt Hamsmith. Si es un problema para implementar, ¿por qué no crear un método de extensión que implemente esto en segundo plano en la clase AppSettings?

Algo como:

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

    }

Dentro del método, busca a través de DictionaryInfoConfigSection usando Linq y devuelve el valor con la clave correspondiente. Sin embargo, necesitará actualizar el archivo de configuración a algo como esto:

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

Se me ocurrió esta solución:

  1. En la aplicación Settings.settings definí una variable ConfigurationBase (con type = string Scope = Application)
  2. Introduje una variable en los atributos de destino en Settings.settings, todos esos atributos tenían que establecerse en Scope = User
  3. En app.xaml.cs leo el valor si ConfigurationBase
  4. En app.xaml.cs reemplacé todas las variables con el valor de ConfigurationBase. Para reemplazar los valores en tiempo de ejecución, los atributos debían establecerse en Scopr = Usuario

No estoy muy contento con esta solución porque tengo que cambiar todos los atributos manualmente, si agrego uno nuevo, tengo que considerarlo en app.xaml.cs.

Aquí un fragmento de código de App.xaml.cs:

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

ACTUALIZAR

Acabo de encontrar una mejora (de nuevo, un fragmento de código de 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);
    }
}

Ahora los reemplazos funcionan para todos los atributos en mi configuración que tienen Type = string y Scope = User. Creo que me gusta así.

ACTUALIZAR2

Aparentemente, configurar Scope = Application no es necesario cuando se ejecuta sobre las propiedades.

anhoppe
fuente
0

Tres posibles soluciones

Sé que voy a llegar tarde a la fiesta, he estado buscando nuevas soluciones al problema de la configuración variable. Hay algunas respuestas que tocan las soluciones que he usado en el pasado, pero la mayoría parecen un poco complicadas. Pensé en mirar mis viejas soluciones y juntar las implementaciones para que pudieran ayudar a las personas que están luchando con el mismo problema.

Para este ejemplo, he usado la siguiente configuración de aplicación en una aplicación de consola:

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

1. Utilice variables de entorno

Creo que la respuesta de autocro autocro lo tocó. Solo estoy haciendo una implementación que debería ser suficiente al compilar o depurar sin tener que cerrar Visual Studio. He usado esta solución en el pasado ...

  • Cree un evento previo a la compilación que utilizará las variables de MSBuild

    Advertencia: Use una variable que no se pueda reemplazar fácilmente, así que use el nombre de su proyecto o algo similar como nombre de variable.

    SETX BaseDir "$(ProjectDir)"

  • Restablecer variables; usando algo como lo siguiente:

    Actualizar variables de entorno en Stack Overflow

  • Use la configuración en su 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. Utilice la interpolación de cadenas:

  • Utilice la función 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 una clase estática, esta es la solución que más utilizo.

  • La implementación

'

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

'

  • La clase 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 de proyecto:

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 previo a la construcción:

Configuración del proyecto -> Eventos de creación

StormChild
fuente