28

ASP.NET Web APIでは、HttpControllerContext インスタンスは、現在の要求の URI など、現在の環境に関する多くの情報を提供します。

サービスがそのような情報 (リクエスト URI など) に依存している場合、その情報をサービスに挿入できる必要があります。

これは、Poor Man の DI を使用して行うのは非常に簡単です。カスタムの IHttpControllerActivator を実装するだけです。

しかし、Castle Windsor では、これが突然非常に難しくなります。以前、この問題を解決する非常に複雑な方法について説明しましたが、それは PerWebRequest のライフスタイルに依存しており、HttpContext.Current が空であるため、このライフスタイルはセルフホスティング シナリオでは機能しないことが判明しました。

これまでのところ、必要な情報をインライン引数としてカスタム IHttpControllerActivator から Resolve メソッドに渡すことで、これを機能させることができました。

public IHttpController Create(
    HttpControllerContext controllerContext,
    Type controllerType)
{
    var baseUri = new Uri(
        controllerContext
            .Request
            .RequestUri
            .GetLeftPart(UriPartial.Authority));

    return (IHttpController)this.container.Resolve(
        controllerType,
        new { baseUri = baseUri });
}

ただし、デフォルトでは、これはすぐに要求された型が引数に依存している場合 (つまり、要求された Controller 自体が に依存している場合baseUri) にのみ機能します。依存関係baseUriが依存関係階層の奥深くに埋もれている場合、インライン引数がより深い層に伝播されないため、デフォルトでは機能しません。

この動作は、カスタム IDependencyResolver (ASP.NET Web API IDependencyResolver ではなく、Castle Windsor IDependencyResolver) で変更できます。

public class InlineDependenciesPropagatingDependencyResolver :
    DefaultDependencyResolver
{
    protected override CreationContext RebuildContextForParameter(
        CreationContext current, Type parameterType)
    {
        if (parameterType.ContainsGenericParameters)
        {
            return current;
        }

        return new CreationContext(parameterType, current, true);
    }
}

デフォルトの実装であるではなく、 がコンストラクタ引数trueとして渡されていることに注意してください。propagateInlineDependenciesfalse

コンテナー インスタンスを InlineDependenciesPropagatingDependencyResolver クラスと結び付けるには、次のように構築する必要があります。

this.container = 
    new WindsorContainer(
        new DefaultKernel(
            new InlineDependenciesPropagatingDependencyResolver(),
            new DefaultProxyFactory()),
        new DefaultComponentInstaller());

これがこの問題に対する最善の解決策なのか、それとももっと簡単な方法があるのか​​ 疑問に思っていますか?

4

2 に答える 2

3

完全を期すために、私が Twitter で Krzysztof Koźmic (Castle Windsor の現在のメンテナー) から得た回答は、質問で概説されている方法が実際に、この特定の目標を達成する正しい方法であることを示しました。

(ただし、Krzysztof さんの twitter アカウントは保護されているため、そのツイートにリンクすることはできません(ツイートは公開されません)。)

于 2012-10-15T21:11:57.947 に答える
2

あなたの InlineDependenciesPropagatingDependencyResolver は実際には、アプリケーションのアーキテクチャにとってかなり重要な何かを隠しているように思えます: 1 つ以上のコンポーネントに、コンテナーから、または動的コンテキストから、静的に確実に解決できない依存関係があることです。

これは、インライン依存関係を Resolve() に渡すときにほとんどの開発者が行う仮定 (依存関係解決の 1 つのレベルのみに渡される) に違反し、特定のシナリオでは、依存関係が他の構成済みサービスを誤ってオーバーライドする可能性があります。(たとえば、同じタイプと名前の依存関係を持つ別のコンポーネントが何レベルも下にある場合)。これは、特定が非常に困難なバグの潜在的な原因である可能性があります。

これの中心にある問題は DI にとって難しい問題であり、IoC が実際には実現可能ではないことを示しています (つまり、依存関係を「プッシュ」する必要があり、コンテナーによって「プル」することはできません)。私には2つのオプションがあるようです:

1) 「反転」を妨げている問題を修正します。つまり、HttpControllerContext/HttpContext をラップし、自己ホスト型のシナリオで必要に応じて動作するようにそのラッパーを拡張し、コンポーネントが直接 HttpControllerContext/HttpContext ではなくそのラッパーに依存するようにします。

2)使用している環境の欠点(「反転」を完全にサポートしていないこと)を反映し、それらの欠点を処理するための回避策を非常に明確にします。あなたのシナリオでは、これにはおそらく、型指定されたファクトリ (インターフェイス) を利用してbaseUriIHttpControllerActivator.Create(). これは、このコンポーネントが依存関係階層のさらに下にある場合、コントローラーができるまで依存関係階層を明示的に構築する必要があることを意味します。

私はおそらく 2 番目のオプションを選択するでしょう。なぜなら、慣習がそれをカットしない場合、私は可能な限り明示的であることを好むからです。

UPDATED コントローラーの typeAがあり、それが componentBに依存し、それが baseUri に依存していると仮定すると、2 番目のオプションは次のようになります。

// Typed factories for components that have dependencies, which cannot be resolved statically
IBFactory bFactory; 
IAFactory aFactory;

public IHttpController Create(HttpControllerContext controllerContext, Type controllerType)
{
    if (controllerType == typeof(A))
    {
        // Special handling for controller where one or more dependencies
        // are only available via controllerContext.
        var baseUri = new Uri(controllerContext.Request.RequestUri.GetLeftPart(UriPartial.Authority));
        B b = this.bFactory.Create(baseUri);
        return this.aFactory.Create(b);
    }
    // Default for all other controllers 
    return (IHttpController)this.container.Resolve(controllerType);
}

重要な点は、これが環境の欠点を明示的に処理し、影響を受ける型を命令的に提供する依存関係のオーバーライドに具体的にバインドし、他の依存関係を誤ってオーバーライドしないようにすることです。

于 2012-07-15T23:48:00.660 に答える