2

次のコードは、Castle.Windsor 2.5.3でそのまま通過しますが、3.1.0にアップグレードすると失敗します。

例外はInvalidProxyConstructorArgumentsExceptionであり、「クラスのプロキシをインスタンス化できません:テスト。パラメーターのないコンストラクターが見つかりませんでした。」と表示されます。

    static void Main(string[] args)
    {
        var container = new WindsorContainer();
        container.Register(Component.For<Interceptor>(),
                           Component.For<Test>().UsingFactoryMethod(() => new Test(""))
                                                .Interceptors<Interceptor>());

        var test = container.Resolve<Test>(); //THROWS IN 3.1.0
    }
}

public class Test
{
    public readonly string S;

    public Test(string s)
    {
        S = s;
    }
}

public class Interceptor : IInterceptor
{
    public void Intercept(IInvocation invocation)
    {
        invocation.Proceed();
    }
}

私の実際のコードでは、テストはファクトリメソッドを使用して構築され、リポジトリに挿入されているMongoDatabaseです。

私の実際のコードでは、AbstractFacilityの継承者を使用してインターセプターを登録しています。このようにして、各コンポーネントのインターセプターを登録する必要はありません。インターセプターの使用の両方の形式は、後の解決で同じように機能/失敗するようです(2.5.3 / 3.1.0)。参考までに、施設の短縮版を次に示します。

public class Facility : AbstractFacility
{
    protected override void Init() { Kernel.ComponentRegistered += KernelComponentRegistered; }

    static void KernelComponentRegistered(string key, IHandler handler)
    {
        if (typeof(IInterceptor).IsAssignableFrom(handler.ComponentModel.Implementation)) return;
        handler.ComponentModel.Interceptors.AddIfNotInCollection(InterceptorReference.ForKey("SomeInterceptor"));
    }
}

Castle.Windsorのソースコードを調べたところ、スローするコードは、指定されたクラスの周りにプロキシをラップすることを期待しているため、パラメーターのないコンストラクターを探しています。ただし、2.5.3では、プロキシ生成コードが実行されることはなく、コンテナは(私の考えでは)非プロキシバージョンのTest/MongoDatabaseに解決されると思います。

だから私が推測する2つの質問:1)何が変わったのですか?2)ファクトリメソッドによって解決されたオブジェクトのプロキシを生成せずにインターセプター登録を保持するにはどうすればよいですか?または、コンポーネントのファクトリメソッドを使用してプロキシを生成するにはどうすればよいでしょうか...

4

2 に答える 2

2

2.5.3 では、ウィンザーはインターセプターの適用に黙って失敗しているようです。3.1.0 では、登録した型にインターセプターを適用できない場合、Windsor は例外をスローします。Windsor は、Dynamic Proxy ライブラリを使用して、ユーザーが要求するインスタンスのプロキシを生成することにより、インターセプター (AOP) をサポートします。したがって、両方のバージョンの問題は、引数のないコンストラクターがないため、指定しているクラスを動的プロキシに変換できないことです。インターセプターが適用されることを期待していた場合、問題が何であるかを理解することははるかに困難になるため、3.1.0 の動作はより正確であると考えます。

サイレントに失敗する 2.5.3 からの動作を維持したい場合は、施設にインターセプターを登録する前に、タイプがプロキシできるかどうかを確認するチェックを追加するだけです。もっと良い方法があるかもしれませんが、私が思いついたのは次のとおりです。

try
{
    ProxyGenerator generator = new ProxyGenerator();
    generator.CreateClassProxy(handler.ComponentModel.Implementation);
    handler.ComponentModel.Interceptors.AddIfNotInCollection(InterceptorReference.ForType<MyInterceptor>());
}
catch {}

これは多くの点でひどいコードですが、慣れ親しんだ動作を再現します。実際にインターセプターをオンにしたい別のクラスがあり、それが呼び出されていない理由を理解するのに苦労している場合は、途中で噛まないように注意してください。

于 2012-09-28T23:58:58.193 に答える
0

ステファンは私を正しい軌道に乗せました。インターセプターが2.5.3でテストに割り当てられていなかったという事実は、バグ/バグを装った機能です。3.1.0の動作はより正確であり、インターセプターが必要なものについて明示的にする必要があります。私の実際のコードにはファシリティが含まれていたため、問題を修正するソリューションは次のとおりです。

public class Facility : AbstractFacility
{
    protected override void Init() { Kernel.ComponentRegistered += KernelComponentRegistered; }

    static readonly List<Type> TypesNotToIntercept = new List<Type>
    {
        typeof(IInterceptor),   //Don't intercept Interceptors
        typeof(MulticastDelegate),  //Func<> and the like
        typeof(LateBoundComponent), //Resolved with a factory, such as MongoDatabase
    };

    static void KernelComponentRegistered(string key, IHandler handler)
    {
        if (TypesNotToIntercept.Any(type => type.IsAssignableFrom(implementation));
            return;
        handler.ComponentModel.Interceptors.AddIfNotInCollection(InterceptorReference.ForKey("SomeInterceptor"));
    }
}
于 2012-09-28T22:30:18.593 に答える