デフォルトのファイルからではなく、カスタムのapp.configファイルから<runtime>を読み取るようにアプリケーションに指示する方法


8

ConsoleApp2というアプリを作成しているとしましょう。

私が使用しているサードパーティのライブラリがあるため、私のデフォルトのapp.configファイルは次のようなコードを生成しています

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

これは、私のソリューションが1つのライブラリの異なるバージョンを参照するため、「このライブラリのoldVersionを探す場合は、newVersionを使用する」ということを全員に伝える必要があるためです。そして、それは大丈夫です。

問題は、いくつかの設定があり、自動生成された設定を削除する別の設定ファイル「test.exe.config」を定義したいことです。

私のアプリに新しい設定ファイルについて知らせるために、私は次のようなコードを使用しています

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

そして、それは(ほぼ)完全に動作します。セクションは正しく読み取られていますが、セクションは私のカスタム構成ファイルで見られていないので、私はそこに「ほぼ」書き込みましたが、代わりにアプリがデフォルトの構成ファイルでそれを探します。後で削除できます。<appSettings><runtime>

では、<runtime>カスタム構成ファイルの情報も読み取るようにアプリケーションに指示するにはどうすればよいですか?


問題の再現方法

私の問題を再現する簡単なサンプルは次のとおりです。

次のように、単一のクラスでClassLibrary2.Net Framework v4.6)というライブラリを作成します。

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

Newtonsoftパッケージへの参照に注意してください。ライブラリにインストールされているのはv10.0.2です。

ここで、Programと呼ばれるクラスを持つConsoleApp2.Net Framework v4.6)と呼ばれるコンソールアプリケーションを作成します。

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

        }
    }
}

このアプリケーションは、Newtonsoftもインストールする必要がありますが、バージョンv12.0.3が異なります。

アプリケーションをデバッグモードでビルドします。次に、ConsoleApp2 / ConsoleApp2 / bin / Debugフォルダーに、次の内容のtest.exe.configというファイルを作成します。

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

同じDebugフォルダーには、次のような内容のデフォルトの設定ファイルConsoleApp2.exe.configもあります。

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

この時点でアプリケーションを実行すると、問題なくコンパイルされ、次のようなコンソールが表示されます。

ここに画像の説明を入力してください

(3)の設定がカスタム構成ファイルから正しく読み取られたことに注意してください。ここまでは順調ですね...

次に、デフォルトの構成ファイルの名前を_ConsoleApp2.exe.configのような名前に変更し、アプリケーションを再度実行します。これでFileLoadExceptionを取得するはずです。

ここに画像の説明を入力してください

繰り返しますが<runtime>、カスタム構成ファイルから情報を読み取るようにアプリケーションに指示するにはどうすればよいですか?


根拠

この質問への回答を探している理由は次のとおりです。

アプリケーションをリリースするとき、すべての.exeファイルと.dllファイルを1つのフォルダーに配置し、カスタム構成ファイル(設定などを含む)を別のフォルダーに配置します。クライアントには同様のファイルがあります。

.exeファイルと.dllファイルが含まれるフォルダーでは、できるだけ少なくしようとするため、可能であればConsoleApp2.exe.config削除する方法を見つけるように求められました。今、前述のバインディングはその構成ファイルに記述されているので、その情報をカスタム構成ファイルに移動しようとしましたが、これまでのところ達成できませんでした:バインディングリダイレクトは常にそのConsoleApp2.exeから読み取られます.configなので、削除するとすぐに例外が発生します...


1
それは不可能です。.configは、プライマリappdomainがCLRホストによって作成される前に適用されます。CLRが開始されて他の処理が行われる前であっても、非常に早い段階で、構成がロックインされます。技術的には独自のAppDomainを作成できますが、.NETCoreと次の.NET 5はそれらをサポートしないため、このようなソリューションの将来はほとんどありません。カスタムホスティングも完全に理想的ではありません。なぜそのようなハックが必要なのかは明らかではありません。リダイレクトのバインドは単なる展開の詳細です。
Hans Passant

こんにちは@HansPassant、情報ありがとうございます。それが文書化されているMSDNドキュメントのどこかを知っていますか?また、それを回答として投稿した場合は、それを受け入れて報奨金を差し上げます。
Deczaloth

2
本、スティーブン・プラチナーは、核心に達するものを書いています。推奨するのは難しいですが、CLRはあまりにも多く変更されました。「それは不可能です」という回答は、SOにアクセスする人には役に立たないと見なされます。なぜこれをする必要があるのか​​説明しなかったので、どこかに行くための明確な方法はありません。
Hans Passant

@HansPassant、最後に「根拠」を追加して質問を編集しました。
Deczaloth

2
@Deczaloth XYの問題<runtime>があるようです。別の構成からセクションを適用するための解決策を見つけようとしていますが、一般的な構成の管理方法が原因で問題が発生します。共通の構成を別の方法で管理する場合、この問題は関係ありません。私の答えを確認して、ランタイムを微調整する代わりに構成変換を使用することを検討してください。
フェニキシル

回答:


6

あなたはおそらくconfig変換を探しています:

背後にある考え方は、構成マネージャーでのデバッグ、リリース、本番、テストなどのVisual Studioで複数の構成を作成し、デフォルトの構成ファイルといわゆる変換を作成することです。

構成マネージャーでは、必要な数だけ構成を作成できることに注意してください。新しいもの追加するには、ソリューション構成(「デバッグ」または「リリース」を示すドロップダウン)をクリックし、「構成マネージャー...」を選択します。それを開くと、現在存在するすべての構成のリストが表示されます。コンボボックス「アクティブソリューション構成」をドロップダウンし、「」を選択<New...>してさらに追加します。

これらの変換は、特定の構成がデフォルト構成と異なるものを指定します。したがって、デフォルト構成ですでに指定したものを繰り返す必要はありません。代わりに、違いを説明するだけです。次に例を示します。

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

これは、キーによって関連する設定を見つけ、設定ファイル内の元の値を置き換えるClientSessionTimeoutこと100によってその値をに設定します(これが追加の変換属性のxdt:Transform="SetAttributes" xdt:Locator="Match(key)"意味です)。(xdt:Transform="Remove"代わりに指定することにより)既存の設定を削除するように指定することもできます。例:

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

リリースではなくデバッグのためにのみ存在する必要があるユーザーIDを削除します(使用可能なオプションの詳細については、ここを参照しください-Web.configについて説明されていますが、App.configにも適用できます)。

加えて、App.Configあなたが持っているファイルの構成ごとに一つのファイルを、すなわちApp.Debug.Configデバッグのために、App.Release.ConfigなどのリリースのためのVisual Studioは、あなたがそれらを作成するのに役立ちます。

私はすでにStackOverflowので回答を作成しているここそこに詳細にそれを説明し、見てください。

Visual Studioでの表示に問題がある場合は、こちらをご覧ください


あなたの根拠について:

トランスフォームは、デフォルトの構成ファイルにトランスフォームファイルを適用して、完全な構成ファイルを作成します。結果のファイルはコンパイルされ、「bin」フォルダに入れられます-他のコンパイルされたファイルと一緒に。したがって、構成「リリース」が選択されている場合、変換された構成ファイルを含むすべてのファイルが「bin \ Release」にコンパイルされます。

そして、構成ファイルの名前は、exeファイルに最後に「.config」を追加したものと同じです(つまり、バイナリフォルダーに「.Release.config」はありませんが、「MySuperCoolApp.exe.config」が作成されます-アプリケーション「MySuperCoolApp.exe」の場合)。

同様に、同じことが他の構成にも当てはまります。各構成は「bin」内にサブフォルダーを作成します。スクリプトを使用している場合、そのサブフォルダーは$(TargetDir)ビルド後のイベントのように参照できます。


こんにちはマット、時間を割いて私を助けてくれてありがとう。以下の回答でわかるように、@ fenixilはconfig変換も提案しました。それにもかかわらず、そして私はそれが魅力的なツールだと思っていても(私は間違いなく私たちのプロジェクトで試してみます!)これが私の質問をどのように解決するかわかりません。私のスーパーシンプルサンプルを拡張して、問題を解決する方法で構成変換を実装できますか?(私の質問の最後にある私の「根拠」に留意してください)
Deczaloth

@Deczaloth-あなたの根拠にいくつかのヒントを追加しました、これがより明確になることを願っています。
Matt

4

構成変換

別の(非ネイティブ)構成ファイルを使用しようとすると問題が発生するため、「適切に」それを置き換えるソリューションを見つけようとしています。私の回答では、少し後退して、なぜそれを置き換えたいのかという理由に焦点を当てたいと思います。質問での説明に基づいて、カスタムアプリケーション設定を定義します。私が正しく理解していれば、それをターゲットプロジェクトにリンクし、「出力にコピー」プロパティを「常に」に設定すると、アプリケーションの近くに表示されます。

新しい構成ファイルをコピーする代わりに、Xdt変換ConsoleApp2.exe.configを使用して、既存の(ネイティブ)ファイルを変換する方法があります。そのためには、変換ファイルを作成し、変換したいセクションのみを宣言します。次に例を示します。

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

このようなアプローチの利点は次のとおりです。

  • 柔軟性:変換は非常に 柔軟性があり、セクションを置き換えたり、セクションをマージしたり、属性を設定/削除したりできます。環境固有(DEV / UAT / PROD)またはビルド固有(Debug / Release)の変換がある場合があります。
  • 再利用性:一度トランスフォームを定義し、必要なすべてのプロジェクトで再利用します。
  • 細分性:必要なものだけを宣言し、構成全体をコピーして貼り付ける必要はありません。
  • 安全性:nugetとmsbuildに「ネイティブ」構成ファイルを管理させる(バインディングリダイレクトを追加するなど)

このアプローチの唯一の欠点は、学習曲線です。構文を学習し、MSBuildで構成に変換を接着する方法を知る必要があります。

.NET Coreは変換をサポートしています。web.configの変換を作成する方法の例を次に示しますが、任意の構成に変換を適用できます。

.NETアプリケーション(.NET Coreではない)を開発している場合は、Slowcheetahを参照することをお勧めします。

変換については多くのリソースと便利なブログがあり、かなり広く使用されています。問題が発生する場合はご連絡ください。

私の観点から見ると、config変換は目標を達成するための適切なソリューションであるため、ランタイムを微調整する代わりに検討することを強くお勧めします。

構成セクションの外部化

それでもappSettingsを共通の場所に保持したい場合は、ConfigSource属性を使用して構成セクションを外部化できます。チェックこれ、この詳細については、スレッドを:

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

AppSettingsセクションには、別のファイルのパラメーターをマージできるFile属性が含まれています。

このオプションを使用すると、構成の特定のセクションを置き換えることができますが、コンテンツ全体を置き換えることはできません。したがって、appSettingsのみが必要な場合、それは完全に適用可能です。ユーザーと共有する共通の場所にappSettingsを含む構成ファイルを配置し、その場所からこのセクションをソースするための構成ファイル(追加fileまたはconfigSource属性)をパッチします。さらにセクションが必要な場合は、それらを別々のファイルとして抽出する必要があります。


おい!私の質問を読んで/答えてくれてありがとう。私は私のオフィスに戻ったらすぐにあなたの提案を試します:)
Deczaloth

私は構成変換を調べました。それは非常に興味深いツールです、私は認めるべきです。それにもかかわらず、これが私の質問にどのように答えるかはわかりません。私が正しく理解している場合は、追加のファイル(つまり、構成変換ファイル)を追加することを提案します。そしてそれを別にしても、<runtime>セクションが自動的にカスタム構成ファイルに書き込まれるように構成変換ファイルを書き込む場合でも、アプリケーションからではなく、そこからバインディングリダイレクトを読み取るようにアプリに指示する方法がわかりませんデフォルトの設定ファイル。わからないことがありましたら教えてください。
Deczaloth

1
config変換の背後にある考え方は、カスタム構成ファイルがなくなったことであり、これにより、別のファイルからランタイム構成を読み取る必要がなくなります。trasformsを使用してアプリの設定(またはその他のもの)をネイティブの構成ファイルに配置するだけです。それは意味がありますか?
フェニキシル

本当に申し訳ないのですが、まだ要点はわかりません。上記の私の超簡単なサンプルを使用して、提案の完全な例を作成できますか?それから私(私が望む)は、Config Transformが私の問題をどのように解決できるかを
非常に

それは完全に何を問題と見なすかに依存します。あなたの観点から見ると、実行時に構成ファイルを置き換えることができないため、<runtime>セクションは別のファイルから消費されます。私はあなたの懸念を再構成しようとしています-ランタイムで構成ファイルを置き換える必要がない場合は、この問題はありません。したがって、私の観点からの問題は、実行時置換を使用してこれらのハックを実行する必要があるように構成を管理する方法です。
フェニキシル

3

別の.configファイルを適切に処理するには、入札リダイレクトを管理するデフォルトのファイルと、アプリケーションパラメータ用の別のファイルをそのまま使用できます。これを行うには、実行時にデフォルトのapp.configを変更すると見栄えがします

自動バインディングリダイレクト生成をシャットダウンして、手作りのapp.configファイルのみを使用することもできます。ここに例があります:同じサードパーティDLLの2つの異なるバージョンを参照する方法が必要です

編集 理論的根拠を考慮して:私が理解しているのであれば、app.exe.configファイルはまったく必要ありません。あなたはすでにカスタム定数をどこかに置いて読むことに成功しています。

バインディングリダイレクトのみが残ります。

それがここで行われているようにあなたは、実行時バインディングリダイレクトを管理することによって、それを取り除くことができます。https://stackoverflow.com/a/32698357/361177 あなたはまた、あなたのコードを見ては、設定ファイルで行われたことにより、設定可能な結合リゾルバを再作成することがあります。

ここに私の2セント:実現可能ですが、それだけの価値はないと思います。

編集2 このソリューションは有望に見えますhttps://stackoverflow.com/a/28500477/361177


ねえ、あなたの答えをありがとう!@BryanSlatnerからの回答への参照のため、私は賛成票を投じましたが、それでもなお、その回避策は私が求めていたものとは完全には一致しません。そして、私が達成したいこと、つまり、アプリケーションに自分のカスタム構成ファイルからバインディングリダイレクトを読み取るように指示することさえできないかもしれません...
Deczaloth

1
構成可能なバインディングリダイレクトプロセスを再構築できる場合があります。AppDomain.CurrentDomain.AssemblyResolveイベントにメソッドを登録し、このメソッドに構成ファイルからバインディングルールを取得させる。
Orace、

1
私はあなたの理論的根拠を再読します。それはあなたの目標(分離されたexeと設定)がプログラムがそれの設定を見つけるハードコードするように要求するように見えます。申し訳ありませんが、それはばかげています。構成ファイルの[相対]パスが変更された場合は、コードを再コンパイルする必要があります。私は分割の利点を理解しています。私にとって最良の解決策は、ランタイム構成と他の構成ファイルへの[相対]パスを含むapp.configファイルです。この他のファイルには、顧客ができる残りの構成が含まれますアプリケーションを微調整します。
オレース

私はあなたの要点を理解します。それにもかかわらず、構成ファイルが配置されるフォルダー構造は、ほぼ10年以上変更されていないため、問題にはなりません。とにかく、私は最後に実際にそのようなアプローチを適用すると思います:「ランタイム構成と他の構成ファイルへの[相対]パスを含むapp.configファイル」。
Deczaloth

@Deczalothはこちらをご覧くださいstackoverflow.com/a/28500477/361177
Orace
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.