web-dev-qa-db-fra.com

Comment dire à Application de lire <runtime> à partir de mon fichier app.config personnalisé au lieu de celui par défaut

Disons que je crée une application appelée ConsoleApp2.

En raison de certaines bibliothèques tierces que j'utilise, mon fichier app.config par défaut génère du code comme

<runtime>
  <assemblyBinding xmlns="urn:schemas-Microsoft-com:asm.v1">
    <dependentAssembly>
      <assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
      <bindingRedirect oldVersion="0.0.0.0-12.0.0.0" newVersion="12.0.0.0" />
    </dependentAssembly>
  </assemblyBinding>
</runtime>

C'est parce que ma solution fait référence à différentes versions d'une bibliothèque, elle doit donc dire à tout le monde: " Hé, si vous en recherchez oldVersion de cette bibliothèque, utilisez simplement newVersion ". Et c'est très bien.

Le problème est que je veux définir un fichier de configuration séparé "test.exe.config" où j'ai certains paramètres et me débarrasser de celui généré automatiquement.

Afin de parler à mon application du nouveau fichier de configuration, j'utilise du code comme

AppDomain.CurrentDomain.SetData("APP_CONFIG_FILE", "test.exe.config");

Et cela fonctionne (presque) parfaitement. Et j'y ai écrit " presque" car bien que la section <appSettings> Soit lue correctement, la section <runtime> N'est pas consultée dans mon fichier de configuration personnalisé, mais l'application le recherche à la place dans le fichier de configuration par défaut, ce qui est un problème car je veux pouvoir le supprimer plus tard.

Alors, comment puis-je dire à mon application de lire également les informations <runtime> De mon fichier de configuration personnalisé?


Comment reproduire le problème

Un exemple simple pour reproduire mon problème est le suivant:

Créez une bibliothèque appelée ClassLibrary2 (. Net Framework v4.6 ) avec une seule classe comme suit

using Newtonsoft.Json.Linq;
using System;

namespace ClassLibrary2
{
    public class Class1
    {
        public Class1()
        {
            var json = new JObject();
            json.Add("Succeed?", true);

            Reash = json.ToString();
        }

        public String Reash { get; set; }
    }
}

Notez la référence au package Newtonsoft . Celui installé dans la bibliothèque est v10.0.2 .

Créez maintenant une application console appelée ConsoleApp2 (. Net Framework v4.6 ) avec une classe appelée Program dont le contenu est simplement le suivant:

using System;
using System.Configuration;

namespace ConsoleApp2
{
    class Program
    {
        static void Main(string[] args)
        {

            AppDomain.CurrentDomain.SetData("APP_CONFIG_FILE", "test.exe.config");

            var AppSettings = ConfigurationManager.AppSettings;

            Console.WriteLine($"{AppSettings.Count} settings found");
            Console.WriteLine($"Calling ClassLibrary2: {Environment.NewLine}{new ClassLibrary2.Class1().Reash}");
            Console.ReadLine();

        }
    }
}

Cette application devrait également avoir installé Newtonsoft , mais dans une version différente v12.0.3 .

Générez l'application en mode débogage. Ensuite, dans le dossier ConsoleApp2/ConsoleApp2/bin/Debug créez un fichier appelé test.exe.config avec le contenu suivant

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6" />
  </startup>
  <runtime>
    <assemblyBinding xmlns="urn:schemas-Microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-12.0.0.0" newVersion="12.0.0.0" />
      </dependentAssembly>
    </assemblyBinding>
  </runtime>
  <appSettings>
    <add key="A" value="1"/>
    <add key="B" value="1"/>
    <add key="C" value="1"/>
  </appSettings>
</configuration>

et notez que dans ce même dossier Debug il y a aussi le fichier de configuration par défaut ConsoleApp2.exe.config avec un contenu comme

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6" />
  </startup>
  <runtime>
    <assemblyBinding xmlns="urn:schemas-Microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-12.0.0.0" newVersion="12.0.0.0" />
      </dependentAssembly>
    </assemblyBinding>
  </runtime>
</configuration>

Si à ce stade, vous exécutez l'application, elle se compilera sans problème et vous devriez voir une console comme

enter image description here

Notez que les (3) paramètres ont été lus correctement à partir de mon fichier de configuration personnalisé. Jusqu'ici tout va bien...

Renommez maintenant le fichier de configuration par défaut en quelque chose comme _ ConsoleApp2.exe.config et réexécutez l'application. Vous devriez maintenant obtenir une FileLoadException .

enter image description here

Encore une fois, comment dire à mon application de lire les informations <runtime> De mon fichier de configuration personnalisé?


Raisonnement

La raison pour laquelle je cherche une réponse à cette question est la suivante:

Lorsque nous publions notre application, nous mettons tous les fichiers .exe et .dll dans un dossier et notre fichier de configuration personnalisé (avec paramètres, etc.) dans un autre, où nos clients ont des fichiers similaires.

Dans le dossier contenant les fichiers .exe et .dll, nous essayons de conserver le moins possible, alors on m'a demandé de trouver un moyen de m'en débarrasser ConsoleApp2.exe.config si possible. Maintenant, puisque les liaisons susmentionnées ont été écrites dans ce fichier de configuration, j'ai juste essayé de déplacer ces informations vers notre fichier de configuration personnalisé ... mais jusqu'à présent, je n'ai pas réussi à le faire: les redirections de liaison sont toujours essayées d'être lues à partir de cela ConsoleApp2.exe.config, donc dès que je le supprime, je reçois des exceptions ...

8
Deczaloth

Vous recherchez probablement des transformations de configuration :

L'idée derrière est que vous créez plusieurs configurations dans Visual Studio comme Debug, Release, Production, Test ... dans le gestionnaire de configuration et un fichier de configuration par défaut plus ce qu'on appelle des transformations.

Notez que vous pouvez créer autant de configurations que vous le souhaitez dans le gestionnaire de configuration. Pour en ajouter de nouvelles, cliquez sur Configurations de solution (la liste déroulante affichant "Debug" ou "Release"), puis sélectionnez "Configuration Manager ...". Ouvrez-le et vous verrez une liste de toutes les configurations actuellement existantes. Déroulez la liste déroulante "Configuration de la solution active" et sélectionnez "<New...>" Pour en ajouter davantage.

Ces transformations spécifient ce qui rend la configuration spécifique différente de la configuration par défaut - vous n'avez donc pas besoin de répéter ce que vous avez déjà spécifié dans la configuration par défaut, au lieu de cela, vous mentionnez simplement les différences, par exemple:

<configuration>
    <appSettings>
        <add key="ClientSessionTimeout" value="100"
            xdt:Transform="SetAttributes" xdt:Locator="Match(key)" />
    </appSettings>
</configuration>

qui trouve le paramètre approprié par sa clé ClientSessionTimeoutet définit sa valeur sur 100 en remplaçant la valeur d'origine dans le fichier de configuration (c'est ce que les attributs de transformation supplémentaires xdt:Transform="SetAttributes" xdt:Locator="Match(key)" signifient) . Vous pouvez également spécifier de supprimer les paramètres existants (en spécifiant xdt:Transform="Remove" À la place), par exemple.

<add key="UserIdForDebugging" xdt:Transform="Remove" xdt:Locator="Match(key)"/>

supprimerait un ID utilisateur qui ne devrait être là que pour le débogage, pas pour la version (pour en savoir plus sur les options disponibles, veuillez regarder ici - décrit pour Web.config, mais également applicable pour App.config ).

En plus du fichier App.Config Vous avez un fichier par configuration, ie App.Debug.Config Pour le débogage , App.Release.Config Pour la version etc. Visual Studio vous aide à les créer.

J'ai déjà créé des réponses dans StackOverflow ici et , qui le décrit en détail, veuillez jeter un œil.

Si vous rencontrez des problèmes pour les afficher dans Visual Studio, jetez un œil ici .


Concernant votre Justification :

Les transformations créent un fichier de configuration complet en appliquant le fichier de transformation au fichier de configuration par défaut. Le fichier résultant est compilé et placé dans le dossier "bin" - avec les autres fichiers compilés. Donc, si vous avez sélectionné une configuration "Release", tous les fichiers, y compris le fichier de configuration transformé, sont compilés dans "bin\Release".

Et le fichier de configuration est nommé exactement comme le fichier exe plus ".config" ajouté à la fin (en d'autres termes, il n'y a pas ".Release.config" dans le dossier binaire, mais un "MySuperCoolApp.exe.config" créé - pour l'application "MySuperCoolApp.exe").

De même, la même chose est vraie pour l'autre configuration - chaque configuration crée un sous-dossier à l'intérieur de "bin" - si vous utilisez des scripts, ce sous-dossier peut être référencé comme $(TargetDir) dans un événement post-construction.

6
Matt

Transformation de la configuration

Étant donné que le problème se produit lorsque vous essayez d'utiliser un autre fichier de configuration (non natif), vous essayez de trouver une solution pour le remplacer correctement. Dans ma réponse, je veux prendre un peu de recul et me concentrer sur la raison pour laquelle vous souhaitez le remplacer. Sur la base de ce que vous avez décrit dans la question, vous l'avez pour définir les paramètres d'application personnalisés. Si j'ai bien compris, vous prévoyez de le lier au projet cible, définissez la propriété "Copier vers la sortie" sur "Toujours" et vous l'aurez à proximité de l'application.

Au lieu de copier le nouveau fichier de configuration, il existe un moyen de transformer celui existant (natif), dans votre cas - ConsoleApp2.exe.config en utilisant transformations Xdt . Pour cela, vous créez un fichier de transformation et n'y déclarez que les sections que vous souhaitez transformer, par exemple:

<configuration xmlns:xdt="http://schemas.Microsoft.com/XML-Document-Transform">
  <appSettings xdt:Transform="Replace">
    <add key="A" value="1"/>
    <add key="B" value="1"/>
    <add key="C" value="1"/>
  </appSettings>
</configuration>

Les avantages d'une telle approche sont:

  • flexibilité: les transformations sont très flexible , vous pouvez remplacer des sections, les fusionner, définir/supprimer des attributs, etc. Vous pouvez avoir des transformations spécifiques à l'environnement (DEV/UAT/PROD) ou des transformations spécifiques à la construction (Debug/Release) .
  • réutilisabilité: définissez la transformation une fois et réutilisez-la dans tous les projets dont vous avez besoin.
  • granularité: vous ne déclarez que ce dont vous avez besoin, pas besoin de copier-coller toute la configuration.
  • sécurité: vous laissez nuget et msbuild gérer le fichier de configuration "natif" (ajouter des redirections de liaison, etc.)

Le seul inconvénient de cette approche est la courbe d'apprentissage: vous devez apprendre la syntaxe et savoir coller les transformations à vos configurations dans MSBuild.

.NET Core prend en charge la transformation, voici un exemple comment créer des transformations pour web.config, mais vous pouvez appliquer des transformations à n'importe quelle configuration.

Si vous développez des applications .NET (pas .NET Core), je vous recommande de regarder Slowcheetah .

Il existe de nombreuses ressources et blogs utiles sur la transformation, elle est plutôt largement utilisée. Veuillez me contacter si vous rencontrez des difficultés.

De mon point de vue, les transformations de configuration sont une bonne solution pour atteindre votre objectif, donc je vous recommande fortement de l'envisager au lieu de modifier le temps d'exécution.

Externaliser les sections de configuration

Si vous souhaitez toujours conserver appSettings dans un emplacement commun, vous pouvez externaliser les sections de configuration avec l'attribut ConfigSource . Vérifiez ce et ce thread pour plus de détails:

// ConsoleApp2.exe.config:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <connectionStrings configSource="../commonConfig/connections.config"/>
</configuration>

// connections.config:
<?xml version="1.0" encoding="utf-8"?>
<connectionStrings>
<add name="MovieDBContext" 
   connectionString="Data Source=(LocalDb)\MSSQLLocalDB;Initial Catalog=aspnet-MvcMovie;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|\Movies.mdf" 
   providerName="System.Data.SqlClient" 
/>
</connectionStrings>

La section AppSettings contient l'attribut File qui vous permet de fusionner les paramètres d'un autre fichier.

Cette option vous permet de remplacer certaines sections de la configuration, mais pas le contenu entier lui-même. Donc, si vous n'avez besoin que d'appSettings, il est totalement applicable - vous venez de mettre le fichier de configuration avec appSettings à un emplacement commun partagé avec l'utilisateur et le fichier de configuration de correctif (ajoutez l'attribut file ou configSource) pour source cette section de cet endroit. Si vous avez besoin de plus de sections, vous devrez les extraire sous forme de fichiers séparés.

4
fenixil

Pour fonctionner correctement avec différents .config, vous pouvez conserver celui par défaut pour gérer les redirections d'enchères et un autre pour les paramètres de votre application. Pour ce faire Changer app.config par défaut au moment de l'exécution est superbe.

Vous pouvez également arrêter la génération de redirection de liaison automatique et utiliser uniquement un fichier app.config fabriqué à la main. Il y a un exemple ici: Besoin d'un moyen de référencer 2 versions différentes de la même DLL tierce

Modifier Tenant compte de la justification: si je comprends bien, vous ne voulez pas du tout du fichier app.exe.config. Vous parvenez déjà à mettre et à lire le contenu personnalisé ailleurs.

Reste seulement la redirection de liaison.

Vous pouvez vous en débarrasser en gérant la redirection de liaison à l'exécution comme cela se fait ici: https://stackoverflow.com/a/32698357/361177 Vous pouvez également recréer un résolveur de liaison configurable en faisant votre code regardez le fichier de configuration.

Mes deux cents ici: c'est faisable mais je ne pense pas que cela en vaille la peine.

Edit 2 Cette solution semble prometteuse https://stackoverflow.com/a/28500477/361177

3
Orace