我们在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();
}
}
如果我们从其他某个类调用MethodA2
或MethodA1
方法,则会拦截它,但MethodA1
从MethodA2
调用时显然不会截获,因为调用来自内部上课。
我们在解决方案Castle Dynamic Proxy not intercepting method calls when invoked from within the class中找到了类似的案例
但是,该解决方案使用new
运算符创建组件和代理,这在我们的情况下不适用,因为我们使用容器。我们可以像上面那样使用这个解决方案吗?或者还有其他方法可以解决这个问题吗?
答案 0 :(得分:3)
当从MethodA1
调用MethodA2
进行拦截时,您需要使用基于继承的拦截(这是因为您使用this
引用来进行调用)。
要首先进行基于继承的拦截,您需要制作MethodA1
和MethodA2
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等心脏病发作)。