问题
We have components registrations in Castle Windsor container like so
void RegisterComponent<TInterface, TImplementation>() {
var component = Component.For<TInterface>().ImplementedBy<TImplementation>();
component.Interceptors<SomeInterceptor>();
container.Register(component);
}
However we got to the problem that when we do a method call from within the class it does not get intercepted. For example we have component like
ServiceA : IService {
public void MethodA1() {
// do some stuff
}
public void MethodA2() {
MethodA1();
}
}
And if we call MethodA2
or MethodA1
methods from some other class it is intercepted, but MethodA1
apparently not intercepted when called from MethodA2
since the call is from within the class.
We have found similar case with the solution Castle Dynamic Proxy not intercepting method calls when invoked from within the class
However the solution features component and proxy creation using new
operator which is not suitable in our case since we are using container. Can we use this solution with component registration like above? Or are there other approaches to solve the problem?
回答1:
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.
回答2:
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>());
}
回答3:
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
.
回答4:
@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
来源:https://stackoverflow.com/questions/35416279/castle-windsor-intercept-method-call-from-within-the-class