2013-04-04 162 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