Skip to content

Adding your first Aspect

Sagi edited this page Apr 3, 2015 · 9 revisions

Please refer to Building your first composite If you haven't read it yet.

Let's say that you need to measure the elapsed time for a method in a specific class.
You'll probably construct a new StopWatch object, call Start method before the code is executed and at the end of the code you'll call the Stop method.

public interface IDeveloper
{
    void Code();
}

public class CSharpDeveloperMixin : IDeveloper
{
    public void Code() {
        var stopwatch = new Stopwatch();

        stopwatch.Start();
        Console.WriteLine("C# coding");
        stopwatch.Stop();
        Console.WriteLine("Elapsed Ticks: {0}", stopwatch.ElapsedTicks);
    }
}

Now you've decided to implement the same pattern in other parts of the system you'll probably find yourself duplicating the code.
When it comes to coding the same operation in different areas of the system you can call it crosscutting concern because it "cut across" multiple areas.

Another problem that lies within the solution is that you mix code that reflects your domain/business intentions and pure technical code that hinders the visibility of domain code.
Aspects abstracts the actual technical code within distinct parts (classes) which lets you focus on the business code while encapsulating the technical code in a different class.

First we need to create the aspect:

public class StopwatchInterceptionAspect : ActionInterceptionAspect
{
    private readonly Stopwatch stopwatch = null;

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

    public override void OnInvoke(ActionInterceptionArgs args) {
        stopwatch.Restart();
        args.Proceed();
        stopwatch.Stop();
        Console.WriteLine("Elapsed Ticks: {0}", stopwatch.ElapsedTicks);
    }
}

We need to create a composite type.
For this purpose and symplicity we will choose to apply the Atom Composite approach by annotating the IDeveloper with TransientCompositeAttribute attribute.

using NCop.Composite.Framework;

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

Applying the aspect can be done using an aspect attribute like MethodInterceptionAspectAttribue attribute and supplying the type of the aspect.

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

We also need to some refactoring by removing all boilerplate code (the stopwatch related code) that hinders the business logic.

public class CSharpDeveloperMixin : IDeveloper
{
    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;

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:
"C# coding"
"Elapsed Ticks: [Number of ticks]"
Your end result of the code should be similar to this:

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

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

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

    public class StopwatchInterceptionAspect : ActionInterceptionAspect
    {
        private readonly Stopwatch stopwatch = null;

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

        public override void OnInvoke(ActionInterceptionArgs args) {
            stopwatch.Restart();
            base.OnInvoke(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();
        }
    }
}