How to call injected method from another aspect in PostSharp
Asked Answered
R

1

1

I'm trying to implement the Observer pattern on a school application using PostSharp.

The situation is as follows: I have a Repository that I want to notify each TesterForm (forms that allow manipulation of the data within the Repository) each time a change is made.

This is the aspect I want to use to add the Observable part to my Repository:

[Serializable]
class ObservableAspect : InstanceLevelAspect
{
    [IntroduceMember]
    List<TesterForm> LT;

    [IntroduceMember]
    public void notifyChange()
    {
         foreach (TesterForm x in LT)
         {
             x.refreshListBoxBuguri();
         }
    }

    [IntroduceMember]
    public void Subscribe(TesterForm t)
    {
        LT.Add(t);
    }
}

Then this aspect applied to every method in Repository that changes the data:

[Serializable]
class ObservableNotify : OnMethodBoundaryAspect
{
    public override void OnExit(MethodExecutionArgs args)
    {
        ((Repository)args.Instance).notifyChange();
    }
}

And then this aspect applied to my TesterForm constructor so it Subscribes to my Repository as soon as it is created:

class ObserverAspect : OnMethodBoundaryAspect
{
    public override void OnExit(MethodExecutionArgs args)
    {
        ((TesterForm)args.Instance).controller.repository.Subscribe((TesterForm)args.Instance);
    }

}

Now, the problem I'm facing is that I have no idea how to call a method that I inject with one aspect, from another aspect (ex: repository.Subscribe from TesterForm) or whether this is even possible.

I have done some research on the PostSharp website, but found no details on such an implementation. Also, Google yielded no useful results.

Thanks in advance for the help!

Additional info: using VS 2013, PostSharp works correctly as I've built other simpler aspects (logging and performance monitoring) that do their job as intended.

Cheers!

Rasmussen answered 6/7, 2015 at 16:41 Comment(0)
B
2

An aspect can access the methods introduced by another aspect using the [ImportMember] attribute. For this to work correctly the importing aspect must also be an instance scoped aspect and you would want to specify the correct order of execution for all the involved aspects.

So, your modified example may look like this:

[AspectTypeDependency( AspectDependencyAction.Order,
                       AspectDependencyPosition.Before,
                       typeof( ObservableNotify ) )]
[Serializable]
class ObservableAspect : InstanceLevelAspect
{
    [IntroduceMember( Visibility = Visibility.Public )]
    public void notifyChange()
    {
        // ...
    }

    // other class members...
}

[Serializable]
class ObservableNotify : OnMethodBoundaryAspect, IInstanceScopedAspect
{
    [ImportMember("notifyChange", IsRequired = true, Order = ImportMemberOrder.AfterIntroductions)]
    public Action notifyChangeMethod;

    public override void OnExit( MethodExecutionArgs args )
    {
        notifyChangeMethod();
    }

    object IInstanceScopedAspect.CreateInstance( AdviceArgs adviceArgs )
    {
        return this.MemberwiseClone();
    }

    void IInstanceScopedAspect.RuntimeInitializeInstance()
    {
    }
}

However, you can also go with a solution that looks a bit cleaner - have all the weaving code in the single ObservableAspect, and mark the methods with a simple ObservableNotify attribute.

[Serializable]
class ObservableAspect : InstanceLevelAspect
{
    private void notifyChange()
    {
        // ...
    }

    // This is the OnExit advice that previously was in a separate aspect.
    [OnMethodExitAdvice]
    [MethodPointcut("SelectMethods")]
    public void OnMethodExit(MethodExecutionArgs args)
    {
        notifyChange();
    }

    // Find all the methods that must be intercepted.
    public IEnumerable<MethodBase> SelectMethods(Type targetType)
    {
        foreach (var methodInfo in targetType.GetMethods())
        {
            if (methodInfo.GetCustomAttributes(typeof (ObservableNotify)).Any())
                yield return methodInfo;
        }
    }
}

class ObservableNotify : Attribute
{
     // This is just a marker attribute used by ObservableAspect.
}
Blinny answered 9/7, 2015 at 16:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.