2

Castle Windsor を IoC コンテナーとして使用するように既存のコードを変更しようとしています。

問題のアプリケーションはドキュメント指向です。そのため、登録時に知ることができないデータ ソースを指定するためのデータに依存するオブジェクト グラフがあり、オブジェクト グラフが解決されるたびに変更されます。container.Resolve()ただし、この依存関係はオブジェクト グラフへのいくつかのレイヤーであるため、Windsor はインライン依存関係を伝達しないため、単に引数として渡すことはできません。

私が代わりに思いついた解決策は、型指定されたファクトリ機能を使用して、グラフ内のすべての依存関係を解決できるファクトリを生成し、Documentのコンストラクタにこのファクトリのインスタンスとデータ ソースを指定する文字列を取得させることです。ファクトリは登録時に指定され、データ ソースは解決時に指定されます。そこからコンストラクターが引き継ぎ、依存関係を解決するためにファクトリーを手動でポーリングします。結果は次のようになります。

public interface IDataSource { /* . . . */ }
public interface IWidgetRepository { /* . . . */ }
public interface IWhatsitRepository { /* . . . */ }

// Implementation generated by Windsor's typed factory facility
public interface IFactory
{
    IDataSource CreateDataSource(string path);
    IWidgetRepository CreateWidgetRepository(IDataSource dataSource);
    IWhatsitRepository CreateWhatsitRepository(IDataSource dataSource);

    void Release(IDataSource dataSource);
    void Release(IWidgetRepository widgetRepository);
    void Release(IWhatsitRepository whatsitRepository);
}

public class Document
{
    private readonly IDataSource _dataSource;
    private readonly IWidgetRepository _widgetRepository;
    private readonly IWhatsitRepository _whatsitRepository;

    public Document (IFactory factory, string dataSourcePath)
    {
        _dataSource = factory.CreateDataSource(dataSourcePath);
        _widgetRepository = factory.CreateWidgetRepository(_dataSource);
        _whatsitRepository = factory.CreateWhatsitRepository(_dataSource);
    }
}

これは完全に機能し、Windsor が依存関係の解決を担当するという目標を達成します。または、少なくとも、登録コードを変更することで、アプリケーションを簡単に再構成できます。現時点では、作成されたものごとに 1 つの呼び出しをcontainer.Resolve()行ってDocumentいますが、罪は 2 番目の Typed Factory で簡単に修正できると信じています。

しかし、それはまだ間違っているように感じます。Windsor は、オブジェクトの新規作成と (多少の) ライフタイムの管理を担当しています。Documentしかし、実際にはこれらの依存関係をのコンストラクターに注入しているわけではありません。代わりに、コンストラクターがそれらをファクトリーから引き出しています。さらに悪いことに、のインスタンスをIDataSourceファクトリ メソッドに渡すことで、オブジェクト グラフの管理を担当します。それは私にとって逆転の巨大な転覆のように思えます。

それで、私は何が欠けていますか?

編集

明確にするために、私が撮影することになっていると思うのは、Documentのコンストラクターが以下のようになることです。

public Document (IDataSource dataSource, IWidgetRepository widgetRepository, IWhatsitRepository whatsitRepository)
{
    _dataSource = dataSource;
    _widgetRepository = widgetRepository;
    _whatsitRepository = whatsitRepository;
}

このように、Windsor は、コンストラクター インジェクションを使用してすべてのオブジェクトの依存関係を提供することを直接制御します。これは実際には元のコードでコンストラクターの署名がどのように見えるかですが、container.Resolve() インライン依存関係パラメーターを伝播しないため、Windsor で動作させることができませんでした。だから私はただすることはできません:

var document = container.Resolve<IDocument>(new { dataSourcePath = somePath });  // throws an exception

Windsor は のコンストラクターに渡さないdataSourcePathため、そのパスでインスタンス化された が他のコンストラクターに渡されること DataSourceを確認することはなおさらです。DataSource

別の質問への回答では、これは設計によるものであることが指摘されています。そうしないと、カップリングが発生します。インターフェイスの実装に特定の依存関係があることを義務付けたり、想定したりすべきではないため、これはノーノーです。悲しいことに、それは私が思いついたコードが間違っていると私が考える別の方法を指摘していFactory.CreateWidgetRepository()ますFactory.CreateWhatsitRepository()

4

2 に答える 2

9

私は正しい解決策を見つけたと信じています。どうやら、利用可能なドキュメントは、最初の 12 回読んだときに私の分厚い頭蓋骨にその概念を突き刺すほど明確な (冗長な?) ものではなかったようです。私のように無力かもしれません。(また、他の誰かが他の/より良い提案をしてくれることを期待して、受け入れる前にしばらく座らせます。)

簡単に言えば、Typed Factory Facility は仕事に適したツールではありません。

--

秘訣は、流暢な登録 API で DynamicParameters 機能を使用することです。これは (かなりまばらに)ここと最後のセクションここに記載されています。

DynamicParameters でできることは、コンテナーがコンポーネントの解決を求められたときに提供されたインライン パラメーターのコレクションを直接変更することです。このディクショナリは、解決パイプラインに渡され、サブ依存関係で使用できるようになります。

DynamicParametersには 3 つのオーバーロードがあり、それぞれがパラメーターとして 1 つのデリゲートを受け取ります。これらのデリゲート型は明示的に文書化されていないため、後世のために、ReSharper (ソースをダウンロードするのが面倒です) によると、宣言は次のようになります。

public delegate void DynamicParametersDelegate(IKernel kernel, IDictionary parameters);
public delegate ComponentReleasingDelegate DynamicParametersResolveDelegate(IKernel kernel, IDictionary parameters);
public delegate ComponentReleasingDelegate DynamicParametersWithContextResolveDelegate(IKernel kernel, CreationContext creationContext, IDictionary parameters);

DynamicParametersDelegateこれは、コンテナーによって管理されないパラメーターを指定するだけでよい、最も基本的なケースです。それはおそらく私にとってはうまくいくでしょうが、最初に複雑なオプションを見つける傾向に合わせて、コンテナから動的パラメーターを手動で引き出すデリゲートを提供するという2番目のオプションのビーラインを作成することになりました。その場合、Windsor がコンポーネントの有効期間を管理しているため、リリースされていることを確認する必要があります。ここで出番です - 最初のものと同じですが、 Windsor が適切なタイミングでコンポーネントをリリースするために使用できる( )DynamicParametersResolveDelegateも返す点が異なります。ComponentReleasingDelegatepublic delegate void ComponentReleasingDelegate(IKernel kernel);

(3 番目の はDynamicParametersWithContextResolveDelegate、おそらく作成コンテキストを変更するためのものです。前の文が何を意味するかを実際に理解するには、Windsor がどのように機能するかについて十分に知りません。そのため、そのままにしておく必要があります。)

これにより、私の例のコンストラクターをより見栄えの良いものに置き換えることができます。

public class Document
{
    private readonly IDataSource _dataSource;
    private readonly IWidgetRepository _widgetRepository;
    private readonly IWhatsitRepository _whatsitRepository;

    public Document (IDataSource dataSource, IWidgetRepository widgetRepository, IWhatsitRepository whatsitRepository)
    {
        _dataSource = dataSource;
        _widgetRepository = widgetRepository;
        _whatsitRepository = whatsitRepository;
    }
}

工場は完全に削除されます。代わりに、次のコンポーネント登録コードに魔法がかかりますIDocument

container.Register(Component.For<IDocument>()
                   .ImplementedBy<Document>()
                   .DynamicParameters( 
                       (k, d) =>
                       {
                           // ask for an IDataSource, passing along any inline
                           // parameters that were supplied in the request for 
                           // an IDocument
                           var ds = k.Resolve<IDataSource>(d);

                           // Add it to the dictionary.  This makes it available
                           // for use when resolving other dependencies in the tree.
                           d.Add("DataSource", ds);

                           // Finally, pass back a delegate which can be used to release it
                           return (r) => r.ReleaseComponent(ds);
                       }));

これで、IDocument探していたコード行とまったく同じを求めることができます。

var document = container.Resolve<IDocument>(new { dataSourcePath = somePath });

DynamicParametersコンテナーは、指定されたパスの をコンテナーに提供するそのデリゲートを呼び出すことによって開始しDataSourceます。そこから、コンテナーはそれ自体で残りを把握できるため、同じインスタンスがDataSource、依存関係グラフ内の他の 3 つのオブジェクトすべてのコンストラクターに渡されます。

于 2012-12-11T17:40:17.063 に答える