Castle Windsor拦截来自班级

时间:2016-02-15 18:13:54

标签: c# castle-windsor ioc-container interceptor castle-dynamicproxy

我们在Castle Windsor容器中有组件注册,如此

void RegisterComponent<TInterface, TImplementation>() {
    var component = Component.For<TInterface>().ImplementedBy<TImplementation>();
    component.Interceptors<SomeInterceptor>();
    container.Register(component);
}

然而,我们遇到的问题是,当我们从类中进行方法调用时,它不会被截获。例如,我们有像

这样的组件
ServiceA : IService {

    public void MethodA1() {
        // do some stuff
    }

    public void MethodA2() {
        MethodA1();
    }

}

如果我们从其他某个类调用MethodA2MethodA1方法,则会拦截它,但MethodA1MethodA2调用时显然不会截获,因为调用来自内部上课。

我们在解决方案Castle Dynamic Proxy not intercepting method calls when invoked from within the class中找到了类似的案例 但是,该解决方案使用new运算符创建组件和代理,这在我们的情况下不适用,因为我们使用容器。我们可以像上面那样使用这个解决方案吗?或者还有其他方法可以解决这个问题吗?

5 个答案:

答案 0 :(得分:3)

当从MethodA1调用MethodA2进行拦截时,您需要使用基于继承的拦截(这是因为您使用this引用来进行调用)。

要首先进行基于继承的拦截,您需要制作MethodA1MethodA2 virtual

然后你可以像这样进行容器注册:

container.Register(Component.For<ServiceA>().Interceptors<SomeInterceptor>());
container.Register(Component.For<IService>().UsingFactoryMethod(c => c.Resolve<ServiceA>()));

首先将您的服务注册为自身应用拦截器(这将在服务上添加基于继承的拦截)。然后,您可以注册将使用先前注册的服务的接口。

答案 1 :(得分:2)

将您的注册更改为以下内容,Windsor应切换到类代理 - 即使用继承进行拦截,而不是组合。

void RegisterComponent<TInterface, TImplementation>() {
    container.Register(Component.For<TInterface,TImplementation>().ImplementedBy<TImplementation>().Interceptors<SomeInterceptor>());
}

答案 2 :(得分:1)

我们使用CreateClassProxy方法为问题Castle Dynamic Proxy not intercepting method calls when invoked from within the class的答案中提出的服务创建代理。 然后我们将获得的代理注册为接口的实现。 所以我们的自定义RegisterComponent方法看起来像这样

private void RegisterComponent<TInterface, TImplementation>()
    where TInterface : class 
    where TImplementation : class, TInterface
{
    var proxyType = new ProxyGenerator().CreateClassProxy<TImplementation>().GetType();
    Container.Register(Component.For<TInterface>().ImplementedBy(proxyType));
}

完整的组件注册

Container = new WindsorContainer();
Container.Kernel.Resolver.AddSubResolver(new CollectionResolver(Container.Kernel));

// Interceptor
Container.Register(Component.For<IInterceptor>().ImplementedBy<SomeInterceptor>().LifestyleTransient());

// Component registrations
RegisterComponent<ISomeService, SomeService>();

当然,您需要拦截的所有方法都应该是virtual,因为使用了基于继承的代理。

但是,此解决方案的缺点是在创建代理对象时无法使用构造函数注入。 请注意,您正在创建&#34; dummy&#34;使用new运算符的代理对象只能获取代理的类型。因此,只有在构造虚拟代理时才能使用构造函数注入,但是当您通过容器解析服务时,注入可以正常工作。因此,这个缺点对于构造逻辑比仅仅依赖性分配更复杂的组件至关重要。如果只需要依赖项分配,则可以在创建虚拟代理之前尝试手动解决容器中的所有依赖项

private object[] ResolveConstructorParameters<TType>()
{
    return typeof(TType).GetConstructors()
                        .Single(c => c.IsPublic)
                        .GetParameters()
                        .Select(p => _container.Resolve(p.ParameterType))
                        .ToArray();
}

然后RegisterComponent将成为

private void RegisterComponent<TInterface, TImplementation>()
    where TInterface : class
    where TImplementation : class, TInterface
{
    var constructorParameters = ResolveConstructorParameters<TImplementation>();
    var proxyType = new ProxyGenerator().CreateClassProxy(typeof(TImplementation), constructorParameters).GetType();
    _container.Register(Component.For<TInterface>().ImplementedBy(proxyType));
}

您也可以使用null填充参数。

答案 3 :(得分:1)

@NikolayKondratyev我调查了https://github.com/castleproject/Windsor/blob/master/src/Castle.Windsor/Windsor/Proxy/DefaultProxyFactory.cs#L110 我已经轻松完成了注册: container.Register(Classes.FromThisAssembly().BasedOn(typeof(IRepositoryBase<,>)) .WithServiceAllInterfaces().WithServiceSelf() .LifestyleTransient());

注意.WithServiceSelf()调用,这实际上会切换基于类的代理

答案 4 :(得分:0)

我知道这是一个旧线程,但是我只是在让Castle拦截器在Blazor WASM中工作时才碰到的(实际上他们确实这么做了,但是请注意...单声道似乎无法支持代理具有任何通用方法的任何类...)。

无论如何,为了解决我的问题,我只是将容器注入到我的类中,并且在需要通过this调用“兄弟方法”的方法中,我只是解决了我的一个新实例接口并在其上调用方法。它不适用于具有共享上下文/瞬态的方案,但是拦截器确实可以完成任务。

在Blazor的客户端WASM应用程序的Program.cs中:

public static async Task Main(string[] args)
{
    WebAssemblyHostBuilder builder = WebAssemblyHostBuilder.CreateDefault(args);

    ...

    builder.ConfigureContainer<IWindsorContainer>(new WindsorServiceProviderFactory(), container =>
    {
        container.Register(Component.For<IInterceptor>()
                                    .ImplementedBy<BlazorInterceptor>()
                                    .Named("BlazorInterceptor").LifestyleTransient());
    });

    ...

    builder.Services.AddScoped<IService, Service>();
    
    ...

    await builder.Build().RunAsync();
}

示例服务和接口实现:

public Interface IService
{
    MethodA(int arg);
    MethodB(int arg);
}

[Interceptor("BlazorInterceptor")]
public class Service : IService
{
    private readonly IServiceProvider _container;

    public Service(IServiceProvider container)
    {
        this._container = container;
    }

    public MethodA(int arg)
    {
        IService service = this._container.GetRequiredService<IService>();
        service.MethodB(arg);
    }

    public MethodB(int arg)
    {
        //should be intercepted...just in a different instance of the service unless you're using singletons...
    }
}

专家:不需要虚拟化方法或使您的DI配置复杂化。 缺点:总体(对无状态存储库很有用,但可能会使EF等心脏病发作)。