Variables dans app.config / web.config


92

Est-il possible de faire quelque chose comme ce qui suit dans les fichiers app.configou web.config?

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

Je veux ensuite accéder à Dir2 dans mon code en disant simplement:

 ConfigurationManager.AppSettings["Dir2"]

Cela m'aidera lorsque j'installe mon application dans différents serveurs et emplacements où je n'aurai à modifier qu'une seule entrée dans mon ensemble app.config. (Je sais que je peux gérer toute la concaténation dans le code, mais je préfère cela de cette façon).


Je pense qu'il parle de définir des variables à utiliser dans les clés appSettings directement dans les fichiers de configuration.
Michaël Carpentier

1
J'ai également vérifié en utilisant la déclaration XML <! ENTITY>, mais elle n'est pas prise en charge en raison de la façon dont MS gère les fichiers web.config.
chilltemp

Merci pour vos efforts. Je préfère ne modifier aucun code. Le code a déjà une instruction disant: string dir2 = ConfigurationManager.AppSettings ["Dir2"]. Je veux seulement nettoyer l'app.config qui dit maintenant value = "D: \ blahdir \ Dir2" au lieu de value = "[MyBaseDir] \ Dir2"
DeeStackOverflow

Réponses:


7

Bonne question.

Je ne pense pas qu'il y en ait. Je pense que cela aurait été assez bien connu s'il y avait eu un moyen simple, et je vois que Microsoft crée un mécanisme dans Visual Studio 2010 pour déployer différents fichiers de configuration pour le déploiement et le test.

Cela dit, cependant; J'ai trouvé que vous dans la ConnectionStringssection avez une sorte d'espace réservé appelé "| DataDirectory |". Vous pourriez peut-être jeter un œil à ce qui se passe là-bas ...

Voici un morceau de le machine.configmontrer:

 <connectionStrings>
    <add
        name="LocalSqlServer"
        connectionString="data source=.\SQLEXPRESS;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|aspnetdb.mdf;User Instance=true"
        providerName="System.Data.SqlClient"
    />
 </connectionStrings>

C'est une information intéressante. Peut-être que les variables sont accessibles en utilisant le symbole de tube ("|")? Hmm .. Je me demande si cela fonctionnera: <add key = "Dir2" value = "| MyBaseDir | \ Dir2" />
DeeStackOverflow

4
La valeur DataDirectory est en fait un élément de données dans AppDomain. Vous pouvez remplacer la valeur en utilisant AppDomain.CurrentDomain.SetData ("DataDirectory", dataPath); Je n'ai pas testé si vous pouvez définir d'autres variables comme celle-ci et les obtenir "autoexpanded" si ...
Peter Lillevold

22

Une alternative légèrement plus compliquée, mais beaucoup plus flexible, consiste à créer une classe qui représente une section de configuration. Dans votre fichier app.config/ web.config, vous pouvez avoir ceci:

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

Ensuite, dans votre code .NET (j'utiliserai C # dans mon exemple), vous pouvez créer deux classes comme celle-ci:

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

Enfin, dans votre code programme, vous pouvez accéder à vos app.configvariables, en utilisant vos nouvelles classes, de cette manière:

DirectoryInfoConfigSection config =
  (DirectoryInfoConfigSection)ConfigurationManager.GetSection("DirectoryInfo");
String dir1Path = config.Directory.Directory1Resolved;  // This value will equal "C:\MyBase\Dir1"

1
Merci mais j'essaye de le faire sans modifier aucun code car c'est pénible à ce stade.
DeeStackOverflow

Il y a une petite erreur dans la dernière ligne de code (sans compter les accolades): "return System.IO.Path.Combine (MyBaseDir, Dir1);" devrait à la place être "return System.IO.Path.Combine (BaseDirectory, Dir1);", ou sinon la méthode devrait être renommée de 'Base Directory' à 'MyBaseDir'
TheWho

16

Vous pouvez accomplir en utilisant ma bibliothèque Expansive . Également disponible sur nuget ici .

Il a été conçu avec cela comme cas d'utilisation principal.

Exemple modéré (en utilisant AppSettings comme source par défaut pour l'expansion des jetons)

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

Utilisez la méthode d'extension .Expand () sur la chaîne à développer:

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

ou

Utilisez l'encapsuleur Dynamic ConfigurationManager "Config" comme suit (appel explicite à Expand () non nécessaire):

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

Exemple avancé 1 (utilisation d'AppSettings comme source par défaut pour l'expansion des jetons)

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

Utilisez la méthode d'extension .Expand () sur la chaîne à développer:

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

4
Je pense que cette réponse est très sous-évaluée !!
Ahmad

Merci Ahmad! Faites-moi savoir comment vous aimez Expansive.
anderly

Bien qu'il s'agisse d'une `` résolution '' d'exécution des paramètres d'application, cela résout mes problèmes d'avoir des paires clé-valeur répétitives. Nous avons considérablement réduit notre maintenance de configuration en utilisant cela. L'utopie absolue ici serait d'avoir un plugin de construction fonctionnant en conjonction avec SlowCheetah. Je donnerais +1 encore si je le pouvais. Super trucs anderly.
Ahmad

Pouvez-vous donner un bref exemple de la façon dont votre bibliothèque pourrait être utilisée pour y parvenir?
Ryan Gates

Pour quiconque vient de trébucher
dessus

4

Je pensais avoir vu cette question.

En bref, non, il n'y a pas d'interpolation de variable dans une configuration d'application.

Vous avez deux options

  1. Vous pouvez lancer le vôtre pour remplacer des variables lors de l'exécution
  2. Au moment de la génération, adaptez la configuration de l'application aux spécificités de l'environnement de déploiement cible. Quelques détails à ce sujet pour gérer le cauchemar de configuration

C'est le bon message. Mon précédent post (même question) n'a pas montré l'exemple d'entrée xml app.config. J'ai vérifié votre lien - c'est trop de travail et je préfère ne pas y passer de temps.Nous avons des app.configs séparés pour différentes boîtes et je veux m'en éloigner.
DeeStackOverflow

3

Vous avez plusieurs options. Vous pouvez le faire avec une étape de construction / déploiement qui traiterait votre fichier de configuration en remplaçant vos variables par la valeur correcte.

Une autre option serait de définir votre propre section de configuration qui prend en charge cela. Par exemple, imaginez ce xml:

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

Vous devez maintenant implémenter cela en utilisant des objets de configuration personnalisés qui géreraient le remplacement des variables pour vous lors de l'exécution.


Je ne vois pas votre xml dans le post (indentez votre ligne de 5 caractères pour pouvoir poster des balises xml - j'ai eu le même problème la dernière fois). En outre, que sont les «objets de configuration personnalisés»? Je préfère le codage zéro pour y parvenir, car les changements de codage à ce stade nous coûteraient beaucoup.
DeeStackOverflow

La configuration personnalisée implique certainement un codage [simple]. Mais à mon humble avis, c'est toujours votre meilleure option. Je n'utilise presque jamais appSettings, préférant plutôt créer une configuration personnalisée pour chaque projet.
Portman

3

Habituellement, je finis par écrire une classe statique avec des propriétés pour accéder à chacun des paramètres de mon 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();
    }

}

Habituellement, je fais également des conversions de type lorsque cela est nécessaire dans cette classe. Il permet d'avoir un accès typé à votre configuration, et si les paramètres changent, vous ne pouvez les éditer qu'à un seul endroit.

Habituellement, le remplacement des paramètres par cette classe est relativement facile et offre une maintenabilité beaucoup plus grande.


3

Vous pouvez utiliser des variables d'environnement dans votre app.configpour ce scénario que vous décrivez

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

Ensuite, vous pouvez facilement obtenir le chemin avec:

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

2

À l'intérieur, <appSettings>vous pouvez créer des clés d'application,

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

Plus tard, vous pouvez accéder à ces valeurs en utilisant:

ConfigurationManager.AppSettings["Keyname"]

Pour utiliser la classe ConfigurationManager, vous devez ajouter une référence à System.Configuration et ajouter une instruction using pour System.Configuration (importations en VB)
cjk

2
L'indication est correcte mais ne répond pas à la question posée.
Michaël Carpentier

1

Je vous suggère DslConfig . Avec DslConfig, vous pouvez utiliser des fichiers de configuration hiérarchiques de Global Config, Config par hôte de serveur à config par application sur chaque hôte de serveur (voir AppSpike).
Si cela est trop compliqué pour vous, vous pouvez simplement utiliser la configuration globale Variables.var
Configurez simplement dans Varibales.var

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

Et obtenez les valeurs de configuration avec

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

0

Je ne pense pas que vous puissiez déclarer et utiliser des variables pour définir des clés appSettings dans un fichier de configuration. J'ai toujours géré les concaténations dans le code comme vous.


0

J'ai un peu de mal avec ce que vous voulez, mais vous pouvez ajouter un fichier de remplacement aux paramètres de l'application, puis définir ce fichier de remplacement par environnement.

<appSettings file="..\OverrideSettings.config">

0

Pour le déploiement de produits pour lesquels nous devons configurer de nombreux éléments avec des valeurs similaires, nous utilisons de petites applications console qui lisent le XML et se mettent à jour en fonction des paramètres transmis. Celles-ci sont ensuite appelées par le programme d'installation après avoir demandé à l'utilisateur le Information requise.


0

Je recommanderais de suivre la solution de Matt Hamsmith. Si c'est un problème à implémenter, pourquoi ne pas créer une méthode d'extension qui l'implémente en arrière-plan sur la classe AppSettings?

Quelque chose comme:

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

    }

Dans la méthode, vous recherchez dans DictionaryInfoConfigSection à l'aide de Linq et renvoyez la valeur avec la clé correspondante. Vous devrez cependant mettre à jour le fichier de configuration pour quelque chose du genre:

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

0

J'ai trouvé cette solution:

  1. Dans l'application Settings.settings j'ai défini une variable ConfigurationBase (avec type = string Scope = Application)
  2. J'ai introduit une variable dans les attributs cibles dans Settings.settings, tous ces attributs devaient être définis sur Scope = User
  3. Dans le app.xaml.cs, j'ai lu la valeur si le ConfigurationBase
  4. Dans app.xaml.cs, j'ai remplacé toutes les variables par la valeur ConfigurationBase. Afin de remplacer les valeurs au moment de l'exécution, les attributs devaient être définis sur Scopr = User

Je ne suis pas vraiment satisfait de cette solution car je dois changer tous les attributs manuellement, si j'en ajoute un nouveau, je dois le regarder dans le app.xaml.cs.

Voici un extrait de code de App.xaml.cs:

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

METTRE À JOUR

Je viens de trouver une amélioration (encore une fois un extrait de code 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);
    }
}

Maintenant, les remplacements fonctionnent pour tous les attributs de mes paramètres qui ont Type = string et Scope = User. Je pense que j'aime ça comme ça.

MISE À JOUR2

Apparemment, la définition de Scope = Application n'est pas requise lors de l'exécution sur les propriétés.


0

Trois solutions possibles

Je sais que je viens en retard à la fête, j'ai cherché s'il y avait de nouvelles solutions au problème des paramètres de configuration variables. Il y a quelques réponses qui touchent aux solutions que j'ai utilisées dans le passé, mais la plupart semblent un peu alambiquées. J'ai pensé examiner mes anciennes solutions et rassembler les implémentations de manière à ce que cela puisse aider les personnes aux prises avec le même problème.

Pour cet exemple, j'ai utilisé le paramètre d'application suivant dans une application console:

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

1. Utilisez des variables d'environnement

Je crois que la réponse d' Autocro Autocro en a parlé. Je fais juste une implémentation qui devrait suffire lors de la construction ou du débogage sans avoir à fermer Visual Studio. J'ai utilisé cette solution dans la journée ...

  • Créez un événement pré-build qui utilisera les variables MSBuild

    Attention: utilisez une variable qui ne sera pas remplacée facilement, alors utilisez le nom de votre projet ou quelque chose de similaire comme nom de variable.

    SETX BaseDir "$(ProjectDir)"

  • Réinitialiser les variables; en utilisant quelque chose comme ce qui suit:

    Actualiser les variables d'environnement en cas de débordement de pile

  • Utilisez le paramètre de votre code:

'

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. Utilisez l'interpolation de chaîne:

  • Utilisez la fonction 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. En utilisant une classe statique, c'est la solution que j'utilise le plus.

  • La mise en oeuvre

»

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

»

  • La classe statique

»

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

»

Code de projet:

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

Événement de pré-construction:

Paramètres du projet -> Événements de construction

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.