私はこの分野で非常に広範囲に取り組んできましたが、範囲内と範囲外の境界線を正確に設定する必要があることを一貫して発見しました。最大限の拡張性を求めるのはリスクですが、それには代償が伴います。そのコストは、規則、ベスト プラクティス、およびおそらく自動化されたビルド チェックの形でのコンパイル時間か、複雑なランタイムになります。
リストしたオプションに対処するには:
バインディング リダイレクトは、ソリューションを実現するための部分的なツールにすぎません。これにより、プログラムで DLL のあるバージョンを別のバージョンに置き換えることができますが、メソッドが変更されたときに何が起こるかという問題を魔法のように解決することはできません。MissingMethodException? ここであなたが持っていないかもしれないものは、依存関係の連鎖です。
アプリケーションが依存関係 'A' をバージョン 1 オブジェクトとして処理している間、内部的にアプリケーションに戻されて v1.0 にキャストされる後のバージョンから何かを作成していることがわかります - 例外が発生します。これは扱いが難しい場合があり、リスクの 1 つにすぎません。
コントラクト アセンブリのバージョンをビルド間で同じに保つ これはエレガントに機能しますが、複雑さをビルド時から実行時まで延期するだけです。変更によってバージョン間の互換性が損なわれないように注意する必要があります。言うまでもなく、アプリケーションが古くなるにつれて、これらのコントラクトに廃止したい宣言がたくさん集まります。最終的に、このファイルは開発者にとって大きく、扱いにくく、混乱を招くものになります。それは、あなたが持っているすべてのエンティティ コントラクトを考慮に入れることさえできません!
これが何を意味するのか、そしてそれがあなたの問題空間をどのようにカバーするのか、私にはよくわかりません。
別の方法として、「SDK」のメジャー リリースごとに新しい契約を作成するという方法があります。これには、主要な機能を一定期間修正するという点で政治的な利点があります。つまり、機能要求を合理的な期待レベルに保つことができ、それを超えるもの (新しい世代の契約が必要) は次のメジャー リリースまで延期されます。 '。ただし、これには機能の設計に注意が必要です。最も明白な要件を先取りできるように、事前に考慮して契約を作成してください。しかし、これはとにかく言うまでもありません...「契約」と呼ばれます理由があります。新しいコントラクトはそれぞれ、バージョンの名前空間 (Company.Product.Contracts.v1_0、Company.Product.Contracts.v1_1) に存在します。
私は自分のコントラクトを連鎖させません (最後のコントラクトを継承するコントラクトの新しいバージョンごと)。そうすることで、バージョン番号を同じに保つという問題に戻ります。チェーンを完全に壊さない限り、機能を完全に取り除くことはできません。
プラグインが読み込まれると、サポートする機能のレベル (コントラクト バージョン) をホストに問い合わせることができます。また、古いホスト/新しいプラグインのシナリオの場合: プラグインをプログラムしてランタイム機能を減らし、より少ないホスト機能に対処します。または単に読み込みを拒否します。プラグインがホストにない機能を利用できるようにする魔法はないため、おそらくこれらのチェックを実行する必要があります。Microsoft の MAF フレームワークは、shim インフラストラクチャを使用してこれを達成しようとしますが、ほとんどの人にとって非常に複雑になります。
そのため、次の点を考慮する必要があります。
- 拡張性の要件を絞り込みます。達成したいことはすべて、継続的なメンテナンスに費用がかかります。
- 機能を廃止する方法を検討する
- コントラクトにはロジック コントラクトだけでなくエンティティも含まれることを忘れないでください。これらはロジック コントラクトとは少し異なる考慮事項があります。
- 各互換性チェックが実行時ではなくコンパイル時に実行される方がよいかどうか (またはその逆) を慎重に検討してください。
- バージョンナンバリング!アセンブリのバージョン番号は実行時の動作に最適です。ファイルのバージョン番号は、バージョン管理で DLL をソースまで追跡するのに役立ちます。両方を利用してください。
- カスタム DLL 解決を行っている場合は、app.config を使用してカスタム DLL の場所を定義します。アセンブリによってイベントが解決されるわけではありません。構成アプローチははるかに予測可能であり、読みやすい Xml で宣言されています。また、Fusion Log Viewer はプローブ チェーンのどこに DLL が挿入されたかを適切に報告しますが、assembly-resolve イベントはコード内のすべてのロジックとルールを非表示にします。唯一の本当の欠点は、app.config を使用すると、アプリケーションが構成ファイルを再読み込みする (通常はアプリを再起動する) まで変更が有効にならないことですが、プラグインの AppDomain 分離を行っている場合は、これを回避することもできます。
あなたの「core.dll」の問題に関係しています...あなたにとって、これは比較的単純な問題だと思います。コア コントラクト DLL 内のすべては、バージョンの名前空間 (上記の Company.Product.v1_0 などを参照) の下に存在する必要があるため、DLL にバージョン番号も含まれていることは実際には理にかなっています。これにより、bin フォルダーにデプロイされたときに DLL が相互に上書きされるという問題が解消されます。GAC に頼らないでください - これは長期的には苦痛です。残念なことに、開発者は常に GAC がすべてをオーバーライドすることを忘れているようであり、これはデバッグの悪夢になる可能性があります。これは、アクセス許可に関する展開シナリオにも影響を与える可能性があります。
DLL 名を同じに保つ必要がある場合は、アプリケーション内に「ローカル gac」を作成できます。これにより、相互に上書きしないように DLL を保存できますが、すべては次の方法で解決できます。ランタイム。app.config の「バインディング リダイレクト」を確認してください (ここで私の回答を参照してください))。これは、アプリケーションの bin フォルダーの下にある「疑似 GAC フォルダー構造」と組み合わせることができます。アプリは、カスタム アセンブリ解決コード ロジックなしで、必要な DLL の任意のバージョンを見つけることができます。以前にサポートされていたすべてのバージョンの core.dll をアプリケーションと共にデプロイします。アプリケーションがバージョン 9 になり、バージョン 7 および 8 のプラグインもサポートすることにした場合、Core.7.dll、Core.8.dll、および Core.9.dll のみを含める必要があります。プラグインのロード ロジックで古いバージョンの Core への依存関係を検出し、プラグインに互換性がないことをユーザーに警告する必要があります。
このトピックにはたくさんのことがあります。他に何かあなたの原因に関連するものを思いついたら、もう一度確認します...