web-dev-qa-db-fra.com

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

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

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 s'il s'agit d'une version validée, puis copie release.app.config dans app.config, sinon, copie debug.app.config dans app.config.

Le problème est que l'application semble obtenir ses paramètres à partir du fichier settings.settings. Je dois donc ouvrir settings.settings dans Visual Studio, ce qui m'indique que les paramètres ont été modifiés. J'accepte les modifications, enregistre les paramètres.settings et les ai reconstruire pour lui faire utiliser les paramètres corrects.

Existe-t-il une méthode meilleure/recommandée/préférée pour obtenir un effet similaire? Ou également, est-ce que j'ai mal abordé la question et existe-t-il une meilleure approche?

161
Gavin

Toute configuration pouvant différer d’un environnement à un autre doit être stockée au niveau machine et non au niveau 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) possède ses propres paramètres uniques dans le répertoire c:\Windows\Microsoft.NET\Framework64\v2.0.50727\CONFIG , vous pouvez alors promouvoir votre code d'application entre les environnements sans aucune modification post-génération.

Et bien entendu, le contenu du répertoire CONFIG au niveau de la machine est contrôlé par la version dans un référentiel différent ou une structure de dossiers différente de celle de votre application. Vous pouvez rendre vos fichiers .config plus conviviaux pour le contrôle du code source 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. (Je n'essaie pas de me vanter, je veux juste vous faire savoir que je n'ai jamais vu de situation où cette approche ne fonctionne pas .)

61
Portman

Cela pourrait aider certaines personnes concernées par Settings.settings et App.config: Surveillez 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 la trouver incorporée dans le fichier lorsque vous l'ouvrez dans un éditeur de texte brut.

Je travaillais sur une application console et si j'avais des valeurs par défaut dans le fichier EXE, l'application ignorait toujours l'emplacement du fichier de configuration dans le même répertoire! C'est un cauchemar et aucune information à ce sujet sur Internet.

51
Roman

Il y a une question connexe ici:

Améliorer votre processus de construction

Les fichiers de configuration sont livrés avec un moyen 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 placez un fichier Local.config sur chaque ordinateur cible, avec uniquement la section appSettings qui contient les remplacements pour cet ordinateur particulier.

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 à partir d'autres ordinateurs et de les archiver quelque part, mais pas dans le cadre des solutions réelles. Chaque développeur place un "ignore" sur le fichier Local.config afin qu'il ne soit pas archivé, ce qui écraserait le fichier de tous les autres.

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

34
Eric Z Beard

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

La syntaxe xml de Nant est un peu étrange, mais une fois que vous la comprenez, 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>
14
Steven Williams

Il me semble que vous pouvez bénéficier du Projet de déploiement Web de Visual Studio 2005 s.

Avec cela, vous pouvez lui dire de mettre à jour/modifier des sections de votre fichier web.config en fonction de la configuration de la construction.

Jetez un oeil à cette entrée de blog de Scott G pour un aperçu rapide/un exemple.

11
Magnus Johansson

Auparavant, nous utilisions 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 nous 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 les valeurs de développeur que vous voulez et ils fonctionneront bien dans votre environnement de développement sans endommager vos systèmes de production. Nous avons constaté que les développeurs sont moins susceptibles de modifier arbitrairement les variables de script de construction lors du test. Par conséquent, les erreurs de configuration accidentelles sont plus rares qu'avec d'autres techniques que nous avons essayées, bien qu'il soit encore nécessaire d'ajouter chaque variable plus tôt dans le processus afin la valeur dev n'est pas forcée à produire par défaut.

8
jasondoucette

Mon employeur actuel a résolu ce problème en plaçant d'abord le niveau de développement (débogage, étape, 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 chaîne de connexion incorrecte 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.

7
Hector Sosa Jr

L’une des solutions qui m’a bien fonctionné consistait à utiliser 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é (édition/stockage intermédiaire/etc ...), je le copiais sur Web.Release.config et le renommais 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>
5
Adam Vigh

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

Utilisez MSBuild avec l'extension de tâches Communauté MSBuild. Il inclut la tâche 'XmlMassUpdate' qui permet de "mettre à jour en masse" les entrées de tout fichier XML une fois que vous lui avez donné le bon nœud.

Implémenter:

1) Vous devez avoir un fichier de configuration qui contiendra vos entrées dev env. c'est le fichier de configuration de votre solution.

2) Vous devez disposer d'un fichier 'Substitutions.xml' contenant uniquement les entrées DIFFERENT (appSettings et ConnectionStrings principalement) pour chaque environnement. Les entrées qui ne changent pas d'un environnement à l'autre ne doivent pas nécessairement ê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 en masse XML et fournissez le bon environnement en tant que paramètre.

Voir 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 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, afin d'éviter toute erreur non récupérable.

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

3
Punit Vora

Vous trouverez une autre solution ici: Le meilleur moyen de basculer la configuration entre les environnements de développement/UAT/Prod dans ASP.NET? qui utilise XSLT pour la transformation du fichier web.config.

Il existe également de bons exemples d'utilisation de NAnt.

3
Aaron Powell

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

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

Je viens de renommer la configuration comme celle que je cible (j'ai un projet avec 8 applications avec beaucoup de configuration base de données/wcf contre 4 événements). Pour déployer avec clickonce dans chacun je change 4 seetings dans le projet et allez. (cela j'aimerais automatiser)

Mon seul souci est de ne pas oublier de "tout nettoyer" après un changement, l'ancienne configuration étant "bloquée" après un changement de nom manuel. (Ce qui, je pense, corrigera votre problème de setting.setting).

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

2
Tony Trembath-Drake

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 pour configurer son comportement en tant que proxy inverse devant Kestrel. Vous devez gérer un web.config si vous souhaitez l'héberger sur IIS .

AppSetting.json:

Pour tout le reste 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, l'environnement est défini sur Développement de sorte 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, 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 de la 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. Voir Configuration des services à l'aide de Documentation des fichiers de configuration et vérifiez également Configuration dans ASP.NET Core.

0
Alper Ebicoglu

Il est indiqué ci-dessus sur asp.net, 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 ici est qu'il est plus facile (pour nous) de mettre à jour la base de données en continu que d'obtenir l'autorisation de mettre à jour en continu les fichiers de production.

EXEMPLE d'un 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
    }
}
0
Prisoner ZERO