0

バックグラウンド

作成される接続文字列に依存するコンポーネントによって実装される多くのサービスがあります-たとえば:

public interface IImportantRepository { ... }

public class ImportantRepository
{
    public ImportantRepository(IOracleConnection connection) { ... }
    public ImportantRepository(string connectionString) { ... } // rarely my constructor of choice, but included for clarity
}

IOracleConnection接続文字列を使用するには、接続文字列との接続を作成する必要があり、string依存関係を解決する必要があるため、一般的には最初のコンストラクターを 2 番目のコンストラクターよりも優先します。

MyIImportantControllerには の単一のコンストラクターとパラメーターがありますIImportantRepository。MyIControllerFactoryはコンポジション ルート (ブートストラップはカウントされません) でありIWindsorContainer.Resolve<T>()、実行時に使用して、ジョブの適切なコントローラーをアクティブ化します。

したがって、ブートストラップ時に全体が次のようになると思います。

Global.Asax => ControllerBuilder.Current => MyControllerFactory : IControllerFactory

そしてこれはリクエスト時に:

IControllerFactory => IImportantController => IImportantRepository => IOracleConnection

動機

現在、あるアプリケーションでは、IOracleConnection(およびそれを構築する基礎となる文字列) は、次の場合に認識される場合があります。

  • アプリケーションが設計されている (テスト用の架空の接続文字列)
  • アプリケーションがコンパイルされます (ビルド変数によって完全に決定される接続文字列)
  • アプリケーションがデプロイされている (web.config変換で設定された接続文字列)
  • アプリケーションがブートストラップされます (構成ソースで設定された接続文字列は 1 回だけ読み取られます)。
  • アプリケーションは特定のタイプのリクエストを処理します (たとえば、「通常」ではなく、「常に」データを表示したいProductionDb) 。
  • Importantアプリケーションは、接続がパラメーター化されている要求 (のChicagoDb代わりに で管理する要求NewYorkDbなど)を処理します。
  • アプリケーションは、セッションまたはユーザーの詳細によって接続が完全に決定される要求を処理します (ユーザーの 10% は存続しDb1、残りはDb2設計上存続します) 。

質問

同じグラフの同じ依存関係のこれらの潜在的な用途をすべて同時に達成するために、DRY に過度に違反することなく保守可能なコードをどのように作成しますか?

4

1 に答える 1

0

プリミティブは、他のコンポーネントが依存する可能性があり、コンテナーに登録する可能性がある他の型とよく似ています。名前を付けたり、静的なコンテキスト (構成ファイル、環境変数、現在のユーザー セッションなど) から解決したりできます。

リストするシナリオはすべて、複合型またはプリミティブ型に適用でき、それらのほとんどはクリーンな DRY ソリューションを備えています。

ただし、プリミティブを扱うにはいくつかの課題があります。

  1. プリミティブをコンテナー内の複合型と区別する 1 つのことは、目的 (サービス ID) の概念だと思います。「接続文字列」は、コンテナーに関する限り、単なる文字列です。これは接続文字列でも、IP アドレスでも、ホスト名でもなく、単なる文字列です。
  2. カスタムリゾルバーの可能性はさておき、プリミティブを接続して他のコンポーネントの依存要件を満たす主な方法は、登録時にプリミティブに名前を付け、依存コンポーネントを登録するときにその名前を参照することです。ただし、名前を使用すると、(少なくとも登録に関しては) 多くの場合不要な結合が課せられます。

したがって、プリミティブを登録したり依存したりしないことを好みます。私は常に、次のようにインターフェイス (通常は「...Settings」で終わる) でそれらをラップします。

public interface IMSSqlDatabaseConnectionSettings 
{
    string ConnectionString { get; set; }
}

public interface IOracleDatabaseConnectionSettings 
{
    string ConnectionString { get; set; }
}

この比較的単純なラッパーは、プリミティブにその目的を示します。これらのプリミティブを必要とするコンポーネントを開発するときに役立ちます。これは、コンポーネント コンストラクターを介して、期待する接続の種類を正確に伝えることができるためです (MSSQL リポジトリ コンポーネントは、Oracle データベースへの接続文字列を受け入れません)。また、登録時に依存関係が何であるかを非常に明確にするのにも役立ちます。

実際には、ほとんどの場合、これらの「設定」オブジェクトの構築を、Krzysztof Koźmicの優れたAppSettingWrapperAttributeのバリアントにオフロードします。(簡単に言うと、構成ファイルの AppSettings からの設定のディクショナリの周りに、厳密に型指定されたラッパーを提供します。) したがって、これらのクラスの実装を実際にコード化することはめったになく、インスタンス化は言うまでもありません。ただし、もちろんそうすることができます (たとえば、テスト用にハードコードされた文字列を提供したり、コンソール アプリに渡されるコマンド ライン パラメーターを形式化したりするため)。

プリミティブをその目的を明示的に示すインターフェイスにラップすることは、多くの場合、登録に名前を使用する必要がないことも意味します。たとえば、'Production Oracle Database' が 1 つしかない場合、必要なコンポーネントはIOracleProductionDatabaseConnectionSettingsすべて、マジック ストリングを必要とせずにコンテナーから正しい設定を解決します。

于 2015-10-14T22:50:40.027 に答える