2013-04-04 104 views
1

我正在Castle Windsor进行拦截实验,并注意到拦截器似乎是作为我的服务接口的装饰器创建的。换句话说,如果我有一个接口“ISomethingDoer”和一个具体的“ConcreteSomethingDoer”,代理实现ISomethingDoer但不从ConcreteSomethingDoer继承。使用Castle Windsor拦截具体实现(与服务相反)使用Castle Windsor

这是好的,毫无疑问的设计,但我想知道的是,我是否可以在我的具体类中拦截受保护的虚拟方法,这些方法不会被公共接口知道。我这样做是为了添加日志记录支持,但我可能想记录一些类的特定内部细节。

在我稍有想象力的测试用例我有这样的:

public interface ISomethingDoer 
{ 
    void DoSomething(int Count); 
} 

[Loggable] 
public class ConcreteSomethingDoer : ISomethingDoer 
{ 
    public void DoSomething(int Count) 
    { 
     for (var A = 0; A < Count; A++) 
     { 
      DoThisThing(A); 
     } 
    } 

    [Loggable] 
    protected virtual void DoThisThing(int A) 
    { 
     ("Doing a thing with " + A.ToString()).Dump(); 
    } 
} 

所以我想要做的是日志调用“DoThisThing”,即使它不是接口的一部分。

我已经设法让Autofac中的这个工作。 (我创建这里Linqpad脚本:http://share.linqpad.net/frn5a2.linq),但正与温莎城堡挣扎(见http://share.linqpad.net/wn7877.linq

在这两种情况下,我的拦截器是一样的,看起来像这样:

public class Logger : IInterceptor 
{ 
    public void Intercept(IInvocation Invocation) 
    { 
     String.Format("Calling method {0} on type {1} with parameters {2}", 
      Invocation.Method.Name, 
      Invocation.InvocationTarget.GetType().Name, 
      String.Join(", ", Invocation.Arguments.Select(a => (a ?? "*null*").ToString()).ToArray())).Dump(); 
      Invocation.Proceed(); 
     "Done".Dump(); 
    } 
} 

我真正想要的要做的是说“任何具有[Loggable]属性的类,都应该使用日志拦截器”。在Autofac例如我已经明确地连接一个记录器进行登记,而与城堡我使用IModelInterceptorsSelector看起来像这样:

public class LoggerInterceptorSelector : IModelInterceptorsSelector 
{ 
    public bool HasInterceptors(ComponentModel Model) 
    { 
     return Model.Implementation.IsDefined(typeof(LoggableAttribute), true); 
    } 

    public InterceptorReference[] SelectInterceptors(ComponentModel Model, InterceptorReference[] Interceptors) 
    { 
     return new[] 
     {  
      InterceptorReference.ForType<Logger>() 
     }; 
    } 
} 

最后,执行此所有的代码是:

var Container = new WindsorContainer(); 

    Container.Register(
     Component.For<Logger>().LifeStyle.Transient 
    ); 

    Container.Kernel.ProxyFactory.AddInterceptorSelector(new LoggerInterceptorSelector()); 

    Container.Register(
     Component.For<ISomethingDoer>() 
     .ImplementedBy<ConcreteSomethingDoer>() 
     .LifeStyle.Transient 
    ); 

    var Doer = Container.Resolve<ISomethingDoer>(); 
    Doer.DoSomething(5); 

运行时,我希望每次调用该方法时都会看到“使用参数x调用方法DoThisThing”。相反,我只接到对DoSomething记录的调用。

我可以看到为什么温莎城堡这样做,但我想知道是否有办法调整行为?

(作为一个侧面说明,我不希望使用温莎自己的拦截器属性,因为我不希望我的介绍根组成的外部依赖性城堡。)

我已经试过专门解决ConcreteSomethingDoer这是有效的,但如果我正在解决ISomethingDoer问题,则不行。

对于这篇较长的文章感到抱歉,同时也很抱歉,因为我对温莎城堡相当陌生!

回答

0

我,你可以这样注册:

Container.Register(
    Component.For<ISomethingDoer, ConcreteSomethingDoer>() 
    .ImplementedBy<ConcreteSomethingDoer>() 
    .LifeStyle.Transient 
); 

这应该由ConcreteSomethingDoer派生创建一个类代理。但是这对动态拦截器不起作用。但是,您可能可以通过创建一个在需要时注册拦截器的工具来解决这个问题。

+0

啊,这是伟大的,作品一种享受 - 非常感谢你!我对此还是有点新鲜感,之前没有遇到类型转发......从我可以收集的这意味着“对于ISomethingDoer,使用与ConcreteSomethingDoer相同的注册”,然后它只向容器注册ConcreteSomethingDoer? (我也可以摆脱“.ImplementedBy”来使用自我注册,这也起作用)。 – John 2013-04-05 15:12:00

+0

注册后,您可以通过ISomethingDoer或ConcreteSomethingDoer来解析ConcreteSomethingDoer。副作用是强制生成类代理。 – Marwijn 2013-04-07 11:30:44

+0

的逗号分隔的有关条款语法简单转发的快捷方式 - 即以下应该工作,以及: Component.For () .ImplementedBy () 的.forward () – Bermo 2014-03-10 02:01:17