Castle Windsor intercept method call from within the class

家住魔仙堡 提交于 2019-11-29 05:14:54

For interception to work on MethodA1 when invoked from MethodA2 you need to be using inheritance based interception (it's because you are using this reference to make the invocation).

To make inheritance based interception possible first you need to make MethodA1 and MethodA2 virtual.

Then you can make container registration like this:

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

First register your service as itself applying interceptors (this will add inheritance based interception over the service). Then you can register the interface which will use service registered earlier.

Change your registration to the following and Windsor should switch to class proxies - i.e. using inheritance for interception, instead of composition.

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

We use CreateClassProxy method to create the proxy for the service as it was proposed in an answer to the question Castle Dynamic Proxy not intercepting method calls when invoked from within the class. Then we register the obtained proxy as an implementation for the interface. So our custom RegisterComponent method looks like this

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));
}

The full component registration is

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>();

And, of course, all methods you need to intercept should be virtual since inheritance based proxy is used.

However a drawback of this solution is that you could not use constructor injection when creating a proxy object. Notice that you are creating "dummy" proxy object with new operator only to get a type of the proxy. Therefore you are unable to use constructor injection only when constructing a dummy proxy, but when you resolve your service via container, injection would work just fine. So this drawback is critical only for components with construction logic being more complex than just assigment of dependencies. If you need only dependency assigments you can try to resolve all dependencies from container manually before creating dummy proxy

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

and then RegisterComponent would become

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));
}

You can also just fill arguments with null.

@NikolayKondratyev I've looked into https://github.com/castleproject/Windsor/blob/master/src/Castle.Windsor/Windsor/Proxy/DefaultProxyFactory.cs#L110 and I've done the registration the easy way: container.Register(Classes.FromThisAssembly().BasedOn(typeof(IRepositoryBase<,>)) .WithServiceAllInterfaces().WithServiceSelf() .LifestyleTransient());

Note .WithServiceSelf() call, this actually switches class-based proxying

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!