2014-06-20 17 views
12

OK, może to zająć dużo czasu. Staram się zrobić dwie rzeczy:Zamawianie realizacji Postsharp Aspects

  • Chcę mieć klasy, która implementuje interfejs trzymając wystąpienie innej klasy, że każde połączenie jest kierowane do.

  • Chcę również przechwycić wszystkie wywołania metod i zrobić coś.

Wykonanie obu na podstawie własnych dzieł świetnie. Łączenie ich wydaje się działać tylko w jednym rozkazie wykonania i jak ma go Murphy, to jest złe (przynajmniej dla mnie).

Chciałbym wstrzyknąć kompozycję najpierw, aby przechwycenie wszystkich połączeń również przechwyciło te, które zostały wcześniej wstrzyknięte.

namespace ConsoleApplication13 
{ 
    using System; 
    using System.Reflection; 

    using PostSharp; 
    using PostSharp.Aspects; 
    using PostSharp.Aspects.Dependencies; 
    using PostSharp.Extensibility; 

    [Serializable] 
    [ProvideAspectRole("COMPOSER")] 
    public sealed class ComposeAspectAttribute : CompositionAspect 
    { 
    [NonSerialized] 
    private readonly Type interfaceType; 

    private readonly Type implementationType; 

    public ComposeAspectAttribute(Type interfaceType, Type implementationType) 
    { 
     this.interfaceType = interfaceType; 
     this.implementationType = implementationType; 
    } 

    // Invoked at build time. We return the interface we want to implement. 
    protected override Type[] GetPublicInterfaces(Type targetType) 
    { 
     return new[] { this.interfaceType }; 
    } 

    // Invoked at run time. 
    public override object CreateImplementationObject(AdviceArgs args) 
    { 
     return Activator.CreateInstance(this.implementationType); 
    } 
    } 

    [Serializable] 
    [ProvideAspectRole("INTERCEPTOR")] 
    [MulticastAttributeUsage(MulticastTargets.Method)] 
    [AspectRoleDependency(AspectDependencyAction.Order, AspectDependencyPosition.After, "COMPOSER")] 
    public sealed class InterceptAspectAttribute : MethodInterceptionAspect 
    { 
    public override void CompileTimeInitialize(MethodBase method, AspectInfo aspectInfo) 
    { 
     base.CompileTimeInitialize(method, aspectInfo); 

     // Warning in VS output 
     Message.Write(method, SeverityType.Warning, "XXX", "Method: " + method.Name); 
    } 

    public override void OnInvoke(MethodInterceptionArgs args) 
    { 
     Console.WriteLine("Intercepted before"); 
     args.Proceed(); 
     Console.WriteLine("Intercepted after"); 
    } 
    } 

    interface ITest 
    { 
    void Call(); 
    } 

    class TestImpl : ITest 
    { 
    public void Call() 
    { 
     Console.WriteLine("CALL remote implemented"); 
    } 
    } 

    [InterceptAspect(AspectPriority = 1)] 
    [ComposeAspect(typeof(ITest), typeof(TestImpl), AspectPriority = 2)] 
    class Test 
    { 
    // this should, after compilation, have all methods of ITest, implemented through an instance of TestImpl, which get intercepted before TestImpl is called 

    public void CallLocalImplementedTest() 
    { 
     Console.WriteLine("CALL local implemented"); 
    } 
    } 


    class Program 
    { 
    static void Main() 
    { 
     var test = new Test(); 

     ITest t = Post.Cast<Test, ITest>(test); 

     Console.WriteLine("TEST #1"); 
     t.Call(); 

     Console.WriteLine("TEST #2"); 
     test.CallLocalImplementedTest(); 

     Console.ReadLine(); 
    } 
    } 
} 

Próbowałem wpłynąć na kolejność wykonywania dwóch aspektach przez

  • AspectRoleDependency, dzięki czemu przechwytywania zależy od kompozytora do uruchomienia pierwszego

  • AspectPriority, również przy kompozytora być pierwszym na mecie.

W testach zawsze dają

TEST #1 
CALL remote implemented 

TEST #2 
Intercepted before 
CALL local implemented 
Intercepted after 

to oczywiście nie działa. Czy masz pojęcie, dlaczego moja kolejność wykonania się nie zmieniła? Czy zrobiłem coś złego, czy brakowało mi szczegółów w dokumentacji? Co mogę zrobić, aby przechwycić również moje metody iniekcji składu?

+0

Trzeba także dodać 'InterceptAspect' w klasie' TestImpl' aby osiągnąć pożądany rezultat . – nemesv

+0

@nemesv Chociaż to by prawdopodobnie działało, czasami klasa TestImpl nie jest moją klasą.Wolałbym rozwiązanie, w którym mogę pozostawić TestImpl tak, jak jest. – nvoigt

+0

Alternatywnie możesz umieścić '[InterceptAspect (AttributeInheritance = MulticastInheritance.Multicast)]' na samym interfejsie 'ITest'. Ale to jest najdalsze, co możesz dostać. Problem polega na tym, że Postsharp wykonuje falowanie IL w jednym kroku i stosuje tylko "InterceptAspect" do metod obecnych w czasie kompilacji, więc nie widzi nowych implementacji interfejsu dodanych za pomocą "ComposeAspect". Tak więc typ dodawany do 'ComposeAspect' powinien już zawierać kod rejestrowania dostarczony przez' InterceptAspect' z umieszczeniem go w klasie 'ITest' lub' TestImpl'. – nemesv

Odpowiedz

2

Przy obecnych aspektach i bieżącej konfiguracji nie można osiągnąć pożądanego rezultatu.

Problem polega na tym, w jaki sposób PostSharp pracy: to nie IL Macha w jednym etapie i ma zastosowanie tylko InterceptAspect do metod, które są obecne w oryginalnym czasie kompilacji więc nie zobaczyć nowe implementacje interfejsu dodawane z ComposeAspect.

W związku z tym nie pomoże żadna kolejność przyjmowania ani podawania ról, priorytetów ani innej konfiguracji.

Jeden obejście byłoby dodać InterceptAspect na wtryskiwanego TestImpl Klasa:

[InterceptAspect] 
class TestImpl : ITest 
    { 
    public void Call() 
    { 
     Console.WriteLine("CALL remote implemented"); 
    } 
    } 

W tym przypadku logika rejestrowania zostaną dodane bezpośrednio TestImpl więc te metody będą zawierały rejestrowanie kiedy będzie się składać do swojej Klasa Test.

Albo jeśli nie oznaczyć każdą implementację można umieścić swój aspekt w interfejsie samego z:

[InterceptAspect(AttributeInheritance = MulticastInheritance.Multicast)] 
interface ITest 
{ 
    void Call(); 
}