Configuration .NET (app.config / web.config / settings.settings)

162

J'ai une application .NET qui a différents fichiers de configuration pour les versions Debug et Release. Par exemple, le fichier debug app.config pointe vers un développement serveur SQL de lequel le débogage est activé et la cible de publication pointe vers le serveur SQL en direct. Il existe également d'autres paramètres, dont certains sont différents dans le débogage / la version.

J'utilise actuellement deux fichiers de configuration distincts (debug.app.config et release.app.config). J'ai un événement de construction sur le projet qui indique que s'il s'agit d'une version de version, copiez release.app.config dans app.config, sinon copiez debug.app.config dans app.config.

Le problème est que l'application semble obtenir ses paramètres à partir du fichier settings.settings, donc je dois ouvrir settings.settings dans Visual Studio qui m'indique ensuite que les paramètres ont changé, j'accepte les modifications, enregistre settings.settings et ai pour reconstruire pour qu'il utilise les paramètres corrects.

Existe-t-il une méthode meilleure / recommandée / préférée pour obtenir un effet similaire? Ou également, ai-je abordé cela complètement faux et y a-t-il une meilleure approche?

Gavin
la source
Je veux désactiver le débogage dans Windows à partir de, j'ai essayé en décochant toutes les cases à cocher dans les paramètres de débogage, mais je pouvais toujours déboguer l'exe de version bin .. N'importe qui m'aide à ce sujet ..
Vinoth Narayan

Réponses:

62

Toute configuration susceptible de différer d'un environnement à l'autre doit être stockée au niveau de la machine et non au niveau de l' application . (Plus d'informations sur les niveaux de configuration.)

Voici les types d'éléments de configuration que je stocke généralement au niveau de la machine:

Lorsque chaque environnement (développeur, intégration, test, étape, live) a ses propres paramètres uniques dans le répertoire c: \ Windows \ Microsoft.NET \ Framework64 \ v2.0.50727 \ CONFIG , alors vous pouvez promouvoir votre code d'application entre les environnements sans aucun modifications post-build.

Et évidemment, le contenu du répertoire CONFIG au niveau de la machine est contrôlé par version dans un référentiel différent ou une structure de dossier différente de celle de votre application. Vous pouvez rendre vos fichiers .config plus conviviaux pour le contrôle des sources grâce à une utilisation intelligente de configSource .

Je fais cela depuis 7 ans, sur plus de 200 applications ASP.NET dans plus de 25 entreprises différentes. (N'essayez pas de vous vanter, je veux juste vous faire savoir que je n'ai jamais vu de situation où cette approche ne fonctionne pas .)

Portman
la source
3
Qu'en est-il d'une situation où vous ne contrôlez pas le serveur Web et ne pouvez donc pas modifier la configuration au niveau de la machine? Les exemples incluent un serveur Web tiers ou un serveur Web partagé entre plusieurs services d'une entreprise.
RationalGeek
1
Ça ne marcherait pas. Mais à l'ère des machines virtuelles, d'Amazon EC2 et des serveurs à 400 $ de Dell, est-ce que quelqu'un fait vraiment quelque chose de sérieux sur les machines partagées? N'essayez pas du tout d'être insensible - je pense vraiment que si vous travaillez sur un serveur Web partagé, vous devriez réévaluer.
Portman
7
La plupart des entreprises dans lesquelles j'ai travaillé avec des sites internes hébergent plusieurs applications sur un serveur - là, une réévaluation devrait être effectuée au niveau de l'entreprise
MPritchard
Plusieurs applications sur un même serveur conviennent tant que les applications sont toutes dans le même «environnement». Par exemple, vous ne voudriez pas que l'instance LIVE d'App1 sur le même serveur que l'instance DEV d'App2. Par exemple, vos paramètres SMTP seraient partagés entre toutes vos applications. En production, vous pointez vers un vrai serveur de messagerie; en développement, vous pointez vers un fichier sur le disque.
Portman
7
Je sais que cela fonctionnera, mais cela va toujours à l'encontre de ce que je recommanderais en essayant d'automatiser le déploiement. Je pense que les paramètres sont spécifiques à l'application, ils doivent être contrôlés par version avec l'application et évoluer avec elle. S'appuyer sur la configuration de la machine ne fait que changer, ce qui, à mon avis, rend les choses plus difficiles. J'aime garder ensemble les choses qui changent ensemble et les déployer ensemble. Si j'ajoute un nouveau paramètre pour Dev, j'aurai probablement besoin d'un paramètre équivalent pour prod.
Miguel Madero
52

Cela pourrait aider certaines personnes traitant Settings.settings et App.config: faites attention à l'attribut GenerateDefaultValueInCode dans le volet Propriétés lors de la modification de l'une des valeurs de la grille Settings.settings dans Visual Studio (Visual Studio 2008 dans mon cas).

Si vous définissez GenerateDefaultValueInCode sur True (True est la valeur par défaut ici!), La valeur par défaut est compilée dans le fichier EXE (ou DLL), vous pouvez le trouver incorporé dans le fichier lorsque vous l'ouvrez dans un éditeur de texte brut.

Je travaillais sur une application console et si j'avais par défaut dans l'EXE, l'application ignorait toujours le fichier de configuration placé dans le même répertoire! Un cauchemar et aucune information à ce sujet sur tout Internet.

romain
la source
7
C'est précisément ce qui m'est arrivé ce week-end dernier. J'ai tiré beaucoup de cheveux en essayant de comprendre pourquoi mon application semblait ignorer mon fichier app.config! Il est censé se connecter à un service Web et l'URL du service se trouve dans mon app.config. À mon insu, lorsque j'ai créé la référence Web, il a également créé un fichier Settings.Settings ET codé en dur la valeur par défaut dans le code. Même lorsque j'ai finalement compris (et supprimé) le fichier de paramètres, cette valeur par défaut est restée dans le hardcode et a été intégrée dans l'exe. TRÈS FRUSTRANT!! Grâce à ce post, je peux maintenant me débarrasser de cette "fonctionnalité"
Mike K
+1 Cette réponse est la plus critique : si vous voulez que votre paramètre aille dans le fichier app.config, définissez son attribut GenerateDefaultValueInCode sur False (la valeur par défaut est True).
Sabuncu
34

Il y a une question connexe ici:

Améliorer votre processus de construction

Les fichiers de configuration permettent de remplacer les paramètres:

<appSettings file="Local.config">

Au lieu d'archiver deux fichiers (ou plus), vous archivez uniquement le fichier de configuration par défaut, puis sur chaque machine cible, vous placez un Local.config, avec uniquement la section appSettings qui contient les remplacements pour cette machine particulière.

Si vous utilisez des sections de configuration, l'équivalent est:

configSource="Local.config"

Bien sûr, c'est une bonne idée de faire des copies de sauvegarde de tous les fichiers Local.config d'autres machines et de les archiver quelque part, mais pas dans le cadre des solutions réelles. Chaque développeur met un "ignorer" sur le fichier Local.config afin qu'il ne soit pas archivé, ce qui écraserait le fichier de tout le monde.

(Vous n'êtes pas obligé de l'appeler "Local.config", c'est exactement ce que j'utilise)

Eric Z Beard
la source
14

D'après ce que je lis, il semble que vous utilisiez Visual Studio pour votre processus de génération. Avez-vous pensé à utiliser MSBuild et Nant à la place?

La syntaxe xml de Nant est un peu étrange, mais une fois que vous l'avez comprise, faire ce que vous avez mentionné devient assez trivial.

<target name="build">
    <property name="config.type" value="Release" />

    <msbuild project="${filename}" target="Build" verbose="true" failonerror="true">
        <property name="Configuration" value="${config.type}" />
    </msbuild>

    <if test="${config.type == 'Debug'}">
        <copy file=${debug.app.config}" tofile="${app.config}" />
    </if>

    <if test="${config.type == 'Release'}">
        <copy file=${release.app.config}" tofile="${app.config}" />
    </if>

</target>
Steven Williams
la source
8

Nous avions l'habitude d'utiliser des projets de déploiement Web, mais nous avons depuis migré vers NAnt. Au lieu de créer des branches et de copier différents fichiers de paramètres, nous intégrons actuellement les valeurs de configuration directement dans le script de construction et les injectons dans nos fichiers de configuration via des tâches xmlpoke:

  <xmlpoke
    file="${stagingTarget}/web.config"
    xpath="/configuration/system.web/compilation/@debug"
    value="true"
  />

Dans les deux cas, vos fichiers de configuration peuvent avoir toutes les valeurs de développeur que vous souhaitez et ils fonctionneront correctement à partir de votre environnement de développement sans casser vos systèmes de production. Nous avons constaté que les développeurs sont moins susceptibles de modifier arbitrairement les variables du script de construction lorsqu'ils testent des choses, de sorte que les erreurs de configuration accidentelles ont été plus rares qu'avec d'autres techniques que nous avons essayées, bien qu'il soit toujours nécessaire d'ajouter chaque variable au début du processus afin que la valeur dev n'est pas poussée à prod par défaut.

Jasondoucette
la source
7

Mon employeur actuel a résolu ce problème en mettant d'abord le niveau de développement (debug, stage, live, etc.) dans le fichier machine.config. Ensuite, ils ont écrit du code pour le récupérer et utiliser le bon fichier de configuration. Cela a résolu le problème avec la mauvaise chaîne de connexion après le déploiement de l'application.

Ils ont récemment écrit un service Web central qui renvoie la chaîne de connexion correcte à partir de la valeur de la valeur machine.config.

Est-ce la meilleure solution? Probablement pas, mais cela fonctionne pour eux.

Hector Sosa Jr
la source
1
En fait, je pense que c'est plutôt élégant, car j'aime garder les différentes versions de la configuration visibles dans une solution, même si elles ne sont pas en ligne.
annakata le
1
C'est une solution très intéressante. J'adorerais regarder un exemple de cela en action.
Mike K
5

L'une des solutions qui m'a bien fonctionné a été l'utilisation d'un WebDeploymentProject. J'avais 2/3 fichiers web.config différents sur mon site, et lors de la publication, en fonction du mode de configuration sélectionné (release / staging / etc ...), je copierais sur le Web.Release.config et le renommerais en web. config dans l'événement AfterBuild et supprimez ceux dont je n'ai pas besoin (Web.Staging.config par exemple).

<Target Name="AfterBuild">
    <!--Web.config -->
    <Copy Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' " SourceFiles="$(SourceWebPhysicalPath)\Web.Release.config" DestinationFiles="$(OutputPath)\Web.config" />
    <Copy Condition=" '$(Configuration)|$(Platform)' == 'Staging|AnyCPU' " SourceFiles="$(SourceWebPhysicalPath)\Web.Staging.config" DestinationFiles="$(OutputPath)\Web.config" />
    <!--Delete extra files -->
    <Delete Files="$(OutputPath)\Web.Release.config" />
    <Delete Files="$(OutputPath)\Web.Staging.config" />
    <Delete Files="@(ProjFiles)" />
  </Target>
Adam Vigh
la source
3

Notre projet a le même problème où nous devions maintenir des configurations pour dev, qa, uat et prod. Voici ce que nous avons suivi (s'applique uniquement si vous êtes familier avec MSBuild):

Utilisez MSBuild avec l'extension de tâches de communauté MSBuild. Il inclut la tâche «XmlMassUpdate» qui peut «mettre à jour en masse» les entrées dans n'importe quel fichier XML une fois que vous lui avez donné le bon nœud pour commencer.

Implémenter:

1) Vous devez avoir un fichier de configuration qui contiendra vos entrées d'environnement de développement; il s'agit du fichier de configuration de votre solution.

2) Vous devez avoir un fichier 'Substitutions.xml', qui ne contient que les entrées qui sont DIFFÉRENTES (appSettings et ConnectionStrings principalement) pour chaque environnement. Les entrées qui ne changent pas dans l'environnement n'ont pas besoin d'être placées dans ce fichier. Ils peuvent vivre dans le fichier web.config de la solution et ne seront pas touchés par la tâche

3) Dans votre fichier de construction, appelez simplement la tâche de mise à jour de masse XML et fournissez le bon environnement en tant que paramètre.

Voir l'exemple ci-dessous:

    <!-- Actual Config File -->
    <appSettings>
        <add key="ApplicationName" value="NameInDev"/>
        <add key="ThisDoesNotChange" value="Do not put in substitution file" />
    </appSettings>

    <!-- Substitutions.xml -->
    <configuration xmlns:xmu="urn:msbuildcommunitytasks-xmlmassupdate">
      <substitutions>
        <QA>
           <appSettings>
            <add xmu:key="key" key="ApplicationName" value="NameInQA"/>
           </appSettings>            
        </QA>
        <Prod>
          <appSettings>
            <add xmu:key="key" key="ApplicationName" value="NameInProd"/>
          </appSettings>            
        </Prod>
     </substitutions>
    </configuration>


<!-- Build.xml file-->

    <Target Name="UpdateConfigSections">
            <XmlMassUpdate ContentFile="Path\of\copy\of\latest web.config" SubstitutionsFile="path\of\substitutionFile" ContentRoot="/configuration" SubstitutionsRoot="/configuration/substitutions/$(Environment)" />
        </Target>

remplacez «$ Environment» par «QA» ou «Prod» en fonction de ce que env. vous construisez pour. Notez que vous devez travailler sur une copie d'un fichier de configuration et non sur le fichier de configuration lui-même pour éviter d'éventuelles erreurs non récupérables.

Exécutez simplement le fichier de construction, puis déplacez le fichier de configuration mis à jour vers votre environnement de déploiement et vous avez terminé!

Pour un meilleur aperçu, lisez ceci:

http://blogs.microsoft.co.il/blogs/dorony/archive/2008/01/18/easy-configuration-deployment-with-msbuild-and-the-xmlmassupdate-task.aspx

Punit Vora
la source
2

Comme vous, j'ai également configuré app.config «multi» - par exemple app.configDEV, app.configTEST, app.config.LOCAL. Je vois quelques-unes des excellentes alternatives suggérées, mais si vous aimez la façon dont cela fonctionne pour vous, j'ajouterais ce qui suit:

J'ai un
<appSettings>
<add key = "Env" value = "[Local] "/> pour chaque application, j'ajoute ceci à l'interface utilisateur dans la barre de titre: à partir de ConfigurationManager.AppSettings.Get ("Env");

Je renomme simplement la configuration en celle que je cible (j'ai un projet avec 8 applications avec beaucoup de configuration base de données / wcf contre 4 evenioments). Pour déployer avec clic une fois dans chacun, je change 4 paramètres dans le projet et c'est parti. (ce que j'aimerais automatiser)

Mon seul problème est de ne pas oublier de «tout nettoyer» après un changement, car l'ancienne configuration est «bloquée» après un changement de nom manuel. (Ce qui, je pense, vous résoudra le problème de réglage).

Je trouve que cela fonctionne très bien (un jour, j'aurai le temps de regarder MSBuild / NAnt)

Tony Trembath-Drake
la source
0

Web.config:

Web.config est nécessaire lorsque vous souhaitez héberger votre application sur IIS. Web.config est un fichier de configuration obligatoire pour IIS afin de configurer son comportement en tant que proxy inverse devant Kestrel. Vous devez maintenir un web.config si vous souhaitez l'héberger sur IIS.

AppSetting.json:

Pour tout ce qui ne concerne pas IIS, vous utilisez AppSetting.json. AppSetting.json est utilisé pour l'hébergement Asp.Net Core. ASP.NET Core utilise la variable d'environnement "ASPNETCORE_ENVIRONMENT" pour déterminer l'environnement actuel. Par défaut, si vous exécutez votre application sans définir cette valeur, elle passera automatiquement par défaut à l'environnement de production et utilisera le fichier «AppSetting.production.json». Lorsque vous déboguez via Visual Studio, il définit l'environnement sur Développement afin qu'il utilise «AppSetting.json». Consultez ce site Web pour comprendre comment définir la variable d'environnement d'hébergement sous Windows.

App.config:

App.config est un autre fichier de configuration utilisé par .NET qui est principalement utilisé pour Windows Forms, les services Windows, les applications console et les applications WPF. Lorsque vous démarrez votre hébergement Asp.Net Core via l'application console, app.config est également utilisé.


TL; DR

Le choix du fichier de configuration est déterminé par l'environnement d'hébergement que vous choisissez pour le service. Si vous utilisez IIS pour héberger votre service, utilisez un fichier Web.config. Si vous utilisez un autre environnement d'hébergement, utilisez un fichier App.config. Consultez la documentation sur la configuration des services à l'aide des fichiers de configuration et consultez également Configuration dans ASP.NET Core.

Alper Ebicoglu
la source
0

Il est dit asp.net ci-dessus, alors pourquoi ne pas enregistrer vos paramètres dans la base de données et utiliser un cache personnalisé pour les récupérer?

La raison pour laquelle nous l'avons fait parce qu'il est plus facile (pour nous) de mettre à jour la base de données en continu que d'obtenir la permission de mettre à jour continuellement les fichiers de production.

Exemple de cache personnalisé:

public enum ConfigurationSection
{
    AppSettings
}

public static class Utility
{
    #region "Common.Configuration.Configurations"

    private static Cache cache = System.Web.HttpRuntime.Cache;

    public static String GetAppSetting(String key)
    {
        return GetConfigurationValue(ConfigurationSection.AppSettings, key);
    }

    public static String GetConfigurationValue(ConfigurationSection section, String key)
    {
        Configurations config = null;

        if (!cache.TryGetItemFromCache<Configurations>(out config))
        {
            config = new Configurations();
            config.List(SNCLavalin.US.Common.Enumerations.ConfigurationSection.AppSettings);
            cache.AddToCache<Configurations>(config, DateTime.Now.AddMinutes(15));
        }

        var result = (from record in config
                      where record.Key == key
                      select record).FirstOrDefault();

        return (result == null) ? null : result.Value;
    }

    #endregion
}

namespace Common.Configuration
{
    public class Configurations : List<Configuration>
    {
        #region CONSTRUCTORS

        public Configurations() : base()
        {
            initialize();
        }
        public Configurations(int capacity) : base(capacity)
        {
            initialize();
        }
        public Configurations(IEnumerable<Configuration> collection) : base(collection)
        {
            initialize();
        }

        #endregion

        #region PROPERTIES & FIELDS

        private Crud _crud; // Db-Access layer

        #endregion

        #region EVENTS
        #endregion

        #region METHODS

        private void initialize()
        {
            _crud = new Crud(Utility.ConnectionName);
        }

        /// <summary>
        /// Lists one-to-many records.
        /// </summary>
        public Configurations List(ConfigurationSection section)
        {
            using (DbCommand dbCommand = _crud.Db.GetStoredProcCommand("spa_LIST_MyConfiguration"))
            {
                _crud.Db.AddInParameter(dbCommand, "@Section", DbType.String, section.ToString());

                _crud.List(dbCommand, PopulateFrom);
            }

            return this;
        }

        public void PopulateFrom(DataTable table)
        {
            this.Clear();

            foreach (DataRow row in table.Rows)
            {
                Configuration instance = new Configuration();
                instance.PopulateFrom(row);
                this.Add(instance);
            }
        }

        #endregion
    }

    public class Configuration
    {
        #region CONSTRUCTORS

        public Configuration()
        {
            initialize();
        }

        #endregion

        #region PROPERTIES & FIELDS

        private Crud _crud;

        public string Section { get; set; }
        public string Key { get; set; }
        public string Value { get; set; }

        #endregion

        #region EVENTS
        #endregion

        #region METHODS

        private void initialize()
        {
            _crud = new Crud(Utility.ConnectionName);
            Clear();
        }

        public void Clear()
        {
            this.Section = "";
            this.Key = "";
            this.Value = "";
        }
        public void PopulateFrom(DataRow row)
        {
            Clear();

            this.Section = row["Section"].ToString();
            this.Key = row["Key"].ToString();
            this.Value = row["Value"].ToString();
        }

        #endregion
    }
}
Prisonnier ZERO
la source