Skip to content

Action Boundary Aspect

Sagi edited this page Apr 9, 2015 · 15 revisions

Let's apply the aspect on the same type that we've created in the Atom Composite example.

using NCop.Mixins.Framework;
using NCop.Composite.Framework;

[TransientComposite]
[Mixins(typeof(CSharpDeveloperMixin))]
public interface IDeveloper
{
    void Code();
}

public class CSharpDeveloperMixin : IDeveloper
{
    public void Code() {
        Console.WriteLine("C# coding");
    }
}

We want to apply the aspect on the Code subroutine (it does not returns a value), therefore we need to create a new aspect that is derived from OnActionBoundaryAspect.

public class StopwatchMethodBoundaryAspect : OnActionBoundaryAspect
{
    private readonly Stopwatch stopwatch = null;

    public StopwatchMethodBoundaryAspect() {
        stopwatch = new Stopwatch();
    }
}

If the Code subroutine accepted one parameter of type string then our aspect would derive from OnActionBoundaryAspect<string>

using NCop.Mixins.Framework;
using NCop.Composite.Framework;

[TransientComposite]
[Mixins(typeof(CSharpDeveloperMixin))]
public interface IDeveloper
{
    void Code(string language);
}

public class StopwatchMethodBoundaryAspect : OnActionBoundaryAspect<string>
{
}

When you derive from a boundary aspect you'll get the option to insert pieces of code in several points in the program (a.k.a join points):

  1. OnEntry - Called before the execution of the method.
  2. OnSuccess - Called when the execution of the method is done executing and there were no exceptions.
  3. OnException - Called when an unhandled exception has been raised.
  4. OnExit - Called after the execution of the method.

Each code piece is called advice. Let's write some code on each advice.

public class StopwatchMethodBoundaryAspect : OnActionBoundaryAspect
{
    private readonly Stopwatch stopwatch = null;

    public StopwatchMethodBoundaryAspect() {
        stopwatch = new Stopwatch();
    }

    public override void OnEntry(ActionExecutionArgs args) {
        Console.WriteLine("{0} OnEntry", args.Method.Name);
        stopwatch.Restart();
    }

    public override void OnException(ActionExecutionArgs args) {
        Console.WriteLine("{0} OnException", args.Method.Name);
    }

    public override void OnExit(ActionExecutionArgs args) {
        Console.WriteLine("{0} OnExit", args.Method.Name);
    }

    public override void OnSuccess(ActionExecutionArgs args) {
        stopwatch.Stop();
        Console.WriteLine("Elapsed Ticks: {0}", stopwatch.ElapsedTicks);
    }
}

In order for NCop to apply StopwatchMethodBoundaryAspect as an aspect we need to annotate the Code subroutine with OnMethodBoundaryAspectAttribute attribute.

using NCop.Mixins.Framework;
using NCop.Composite.Framework;
using NCop.Aspects.Framework;

[TransientComposite]
[Mixins(typeof(CSharpDeveloperMixin))]
public interface IDeveloper
{
    [OnMethodBoundaryAspect(typeof(StopwatchMethodBoundaryAspect))]
    void Code();
}

An Aspect can be placed also on the Mixin's subroutine

public class CSharpDeveloperMixin : IDeveloper
{
    [OnMethodBoundaryAspect(typeof(StopwatchMethodBoundaryAspect))]
    public void Code() {
        Console.WriteLine("C# coding");
    }
}

The last thing that we have to do is create a CompositeContainer which will handle two things:

  1. Craft the real implementation in runtime.
  2. Act as a Dependency Injection Container that will resolve our type.
using System;
using NCop.Mixins.Framework;
using NCop.Composite.Framework;
using NCop.Aspects.Framework;

class Program
{
    static void Main(string[] args) {
        IDeveloper developer = null;
        var container = new CompositeContainer();

        container.Configure();
        developer = container.Resolve<IDeveloper>();
        developer.Code();
    }
}

The expected output should be:
"Code OnEntry"
"C# coding"
"Elapsed Ticks: [Number of ticks]"
"Code OnExit"
Your end result of the code should be similar to this:

using System;
using NCop.Aspects.Framework;
using NCop.Mixins.Framework;
using NCop.Composite.Framework;
using System.Diagnostics;

namespace NCop.Samples
{
    [TransientComposite]
    [Mixins(typeof(CSharpDeveloperMixin))]
    public interface IDeveloper
    {
        [OnMethodBoundaryAspect(typeof(StopwatchMethodBoundaryAspect))]
        void Code();
    }

    public class CSharpDeveloperMixin : IDeveloper
    {
        public void Code() {
            Console.WriteLine("C# coding");
        }
    }

    public class StopwatchMethodBoundaryAspect : OnActionBoundaryAspect
    {
        private readonly Stopwatch stopwatch = null;

        public StopwatchMethodBoundaryAspect() {
            stopwatch = new Stopwatch();
        }

        public override void OnEntry(ActionExecutionArgs args) {
            Console.WriteLine("{0} OnEntry", args.Method.Name);
            stopwatch.Restart();
        }

        public override void OnException(ActionExecutionArgs args) {
            Console.WriteLine("{0} OnException", args.Method.Name);
        }

        public override void OnExit(ActionExecutionArgs args) {
            Console.WriteLine("{0} OnExit", args.Method.Name);
        }

        public override void OnSuccess(ActionExecutionArgs args) {
            stopwatch.Stop();
            Console.WriteLine("Elapsed Ticks: {0}", stopwatch.ElapsedTicks);
        }
    }

    class Program
    {
        static void Main(string[] args) {
            IDeveloper developer = null;
            var container = new CompositeContainer();

            container.Configure();
            developer = container.Resolve<IDeveloper>();
            developer.Code();
        }
    }
}