SheepAOP Part 4: Integrating with IoC containers

This Series

  1. Getting Started with SheepAOP
  2. Pointcuts and SAQL Basics
  3. Aspects Lifecycles & Instantiations
  4. Integrating with IoC containers
  5. Aspects Inheritance & Polymorphism
  6. Attributive Aspects (ala PostSharp)
  7. Unit-testing your aspects
  8. Extending SheepAop

Aspect Factory

In the previous post, we have discussed at great length about various SheepAop lifecycles and how they affect the specific points at which SheepAop should instantiate new aspect objects. Whenever your lifecycle strategy determines that a new aspect needs to be created, by default SheepAop will look for a default parameterless constructor in your aspect class.

SheepAop lets you to can change this behavior and use your own IoC container to manage the instantiations of your aspects and wire up their dependencies. This is done by implementing your own custom AspectFactory. For example, the following code will hook SheepAop with StructureMap.

public class StructureMapAspectFactory: AspectFactory
{
   private readonly Container _container;
   public StructureMapAspectFactory(Container container)
   {
      _container = container;
   }

   public override object CreateInstance(Type type)
   {
      return _container.GetInstance(type);
}
}

And you add the following code to the entry point of your program (i.e. Global.asax in a web application)

AspectFactory.Current = new StructureMapAspectFactory(container);

All pretty straightforward. Now if you take the example aspect class from few posts back…

[Aspect]
public class PurchasingNotificationAspect
{
   private readonly INotificationService _notifier;
   public PurchasingNotificationAspect(INotificationService notifier)
   {
      _notifier = notifier;
   }

   [SelectPropertySets("Name:'StockQty' & InType:Name:'Product'")]
   public static void SettingProduct(){}

   [Around("SettingProduct")]
   public void CheckStockForNotification(PropertySetJoinPoint jp)
   {
      jp.Proceed();
      if ((int)jp.Value < 5)
      {
         _notifier.Send(Notice.Restock, (Product)jp.This);
      }
   }
}

… StructureMap will now handle the instantiations of this aspect class, including the wiring of the INotificationService dependency via the constructor.

I highly recommend to always register all your aspect classes using transient configuration on your IoC container, and to leave it to SheepAop to handle the lifecycle management of the instances.

SheepAOP Part 3 – Aspect Lifecycles

This Series

  1. Getting Started with SheepAOP
  2. Pointcuts and SAQL Basics
  3. Aspects Lifecycles & Instantiations
  4. Integrating with IoC containers
  5. Aspects Inheritance & Polymorphism
  6. Attributive Aspects (ala PostSharp)
  7. Unit-testing your aspects
  8. Extending SheepAop

This Post

In this post, I will introduce you with the notion of aspect-lifecycle in SheepAop, a feature that I have claimed more than once to be among the utmost importance in an AOP framework for taking AOP’s real-world use beyond the highly clichéd logging and transaction toy-demo. Aspect-lifecycles offer some elegant answers to a whole array of real-world problems that are not normally solvable using conventional AOP solutions.

This post will take you through a generous amount of those examples to demonstrate some real-world applications of AOP in general, and of aspect-lifecycles in particular. No more Logging, Transaction, or SecurityAuthorization demo, I believe we’ve had enough of that 🙂

Overview

By default, SheepAop uses Singleton lifecycle, which means that only one instance of an aspect type exists. This is the simplest form of aspect instance. The state of a Singleton aspect is effectively global. Usually this arrangement is well suited for stateless aspects, as well as aspects with an inherently global state, such as caching or resource-pool.

But in some situations, especially when you are creating reusable aspects, you want to associate the aspect’s state in a very specific manner (e.g. per individual object, or a particular class, or per scope of a control flow).
The aspect-lifecycle mechanism in SheepAop offers various ways to control the lifecycles of aspect instances, and offers many interesting and powerful design choices.

There are several kinds of aspect lifecycles in SheepAop:

  • Singleton (default)
  • Transient
  • Per object (PerThis and PerTarget*)
  • Per control-flow
  • Per type*

*) Not yet supported

Note: It’s very trivial to implement your own aspect-lifecycle, e.g. WebRequestLifecycle, or WebSessionLifecycle. We’ll cover that on “Extending SheepAop” later in the series.

Singleton Lifecycle (Default)

Singleton-lifeycle is in effect when you don’t specify any lifecycle in your aspect class declaration.

[Aspect]
public class MyAspect
{
}

You can also declare it explicitly using SingletonAspect, which makes no difference whatsoever.

[SingletonAspect]
public class MyAspect
{
}

Let’s write a simple toy program that creates 2 Account objects and calls their methods.

public class MyProgram
{
   public static void Main()
   {
      var account1 = new SavingAccount(12345);
      var account2 = new SavingAccount(77777);

      account1.Credit(200);
      account1.Debit(100);

      account2.Credit(200);
      account2.Debit(100);
   }
}

And apply some aspect:

[Aspect]
public class MyLoggingAspect
{
   var _logger = logger;
   public MyLoggingAspect(ILogger logger)
   {
      _logger = logger;
      _logger.Trace("> Creating MySingletonAspect instance");
   }

   [SelectMethods("'void SavingAccount::*(System.Int32)'")]
   private void AccountPointcut();

   [Around("AccountPointcut")]
   pulbic void AroundAccountMethods(MethodJoinpoint jp)
   {
      _logger.Trace(">> Calling {0}({1}) on {2}", jp.Method, jp.Args[0], jp.This);
      jp.Execute();
   }
}

PS: The ILogger parameter on the constructor is injected by your IoC container. We’ll look into how to hook SheepAop with your IoC container in the next post or two.

The output is:

> Creating MyLoggingAspect instance
>> Calling Credit(200) on SavingAccoung{12345}
>> Calling Debit(100) on SavingAccoung{12345}
>> Calling Credit(200) on SavingAccoung{77777}
>> Calling Debit(100) on SavingAccoung{77777}

Only one MySingleAspect instance will ever be created over the course of the program, which in this case also means that you will always append all log entries to only one ILogger instance.
(All previous examples in the series so far use Singleton lifecycle by default, so I won’t bother you with another example on Singleton aspects).

Transient Lifecycle

Transient lifecycle will create a new aspect instance for every time a join-point is hit.
Let’s use the same toy program from the example above, and just slightly change the aspect declaration to the following:

[TransientAspect]
public class MyLoggingAspect
{
   /* unchanged */
}

Now the output will become like the following:

> Creating MyLoggingAspect instance
>> Calling Credit(200) on SavingAccoung{12345}
> Creating MyLoggingAspect instance
>> Calling Debit(100) on SavingAccoung{12345}
> Creating MyLoggingAspect instance
>> Calling Credit(200) on SavingAccoung{77777}
> Creating MyLoggingAspect instance
>> Calling Debit(100) on SavingAccoung{77777}

A new MyTransientAspect instance is created for every join-point hit, which will append a log entry to a potentially different ILogger instance each time.
There really are not many realistic scenarios where Transient lifecycle would be necessary, but it can be useful especially when you want to make use of the lifecycle-capability provided by your IoC container. More about IoC integration in the next post.

Per Object Lifecycles (PerThis and PerTarget)

If an aspect A is defined AspectPerThis(Pointcut), then one object of type A is created for every instance that is the executing object (i.e., “This”) at any of the join points picked out by the Pointcut.
The advice defined in aspect A will run only at a join point where the currently executing object has been associated with an instance of A.

So if we modify our first toy (logging-aspect) example as follows:

[PerThis("AccountPointcut")]
public class MyLoggingAspect
{
   /* unchanged */
}

.. the output will become:

> Creating MyLoggingAspect instance
>> Calling Credit(200) on SavingAccoung{12345}
>> Calling Debit(100) on SavingAccoung{12345}
> Creating MyLoggingAspect instance
>> Calling Credit(200) on SavingAccoung{77777}
>> Calling Debit(100) on SavingAccoung{77777}

Everytime the “AccountPointcut” hits a join point, SheepAop will look at the object bound to This (the current SavingAccount instance), and create a new aspect instance if there is not already a MyLoggingAspect in existence for that SavingAccount instance. The aspect instance is eligible for garbage collection at the same time as the object it is associated with.

The PerTarget model works in a very similar manner to PerThis. If an aspect A is defined AspectPerTarget(Pointcut), then one object of type A is created for every object that is the Target object of the join points picked out by Pointcut. The advice defined in A will run only at a join point where the target object has been associated with an instance of A.
Note: Target object is only applicable to join-points matched using call-pointcuts, not member-pointcuts. Therefore consequently only call-pointcuts can be used with PerTarget lifecycle.

Example: Concurrent Object

In this example, we will create an aspect to convert your non-thread-safe class into a thread-safe one by applying the read-write lock pattern, hence allowing concurrent access to the objects. This pattern requires one lock for each object instance. The per-object lifecycles provide a mechanism to associate a new aspect isntance with each execution (‘This’) or ‘Target’ object.

In the following example, we will use AspectPerThis lifecycle to associate each of your ShoppingCart objects with a new ReadWriteLockAspect.

[AspectPerThis("Readings", "Writings")]
public class ReadWriteLockAspect
{
   [SelectMethod("'* ShoppingCart::Get*(*)'")]
   [SelectPropertyGet("'* ShoppingCart::*'")]
   protected void Readings(){}

   [SelectMethod("'void ShoppingCart::*(*)'")]
   [SelectPropertySet("'void ShoppingCart::*(*)'")]
   protected void Writings(){}

   private ReaderWriterLockSlim _lock = new ReaderWriterLockSlim();

   [Around("Readings")]
   public object LockRead(JoinPoint jp)
   {
      try
      {
         _lock.EnterReadLock();
         return jp.Execute();
      }
      finally()
      {
         _lock.ExitReadLock();
      }
   }

   [Around("Writings")]
   public void LockWrite(JoinPoint jp)
   {
      try
      {
         _lock.EnterWriteLock();
         jp.Execute();
      }
      finally()
      {
         _lock.ExitWriteLock();
      }
   }
}

Note: Ideally, given that the read/write lock pattern itself is reusable, you would define this aspect as abstract (with abstract pointcuts) so you can reuse the aspect. We will cover this in part 5 (“Aspect Inheritence”).

So now each of your ShoppingCart instance will be assigned with a ReadWriteLockAspect, which will lock and release read/write access to your object. We have just converted our ShoppingCart to be thread-safe without introducing any additional noise into your actual code.

Examples: Timing-Out DbConnection Instances

In this example, we will write an aspect to limit the duration for which each DbConnection instance may keep its connection open. We’ll set a timer to forecfully close DbConnection instances that have been left open for over a certain period of time. Since the timer is associated with each connection object, we’ll need an aspect with per-object lifecycle (in this case, PerTarget).

[AspectPerTarget("OpeningConnection")]
public class ConnectionTimeoutAspect
{
   [SelectCallMethods("'void System.Data.DbConnection::Open()'")]
   protected void OpeningConnection(){}

   [SelectCallMethods("'void System.Data.DbConnection::Close()'")]
   protected void ClosingConnection(){}

   private const int TIMEOUT = 1000;
   private Timer timer;

   [Around("OpeningConnection")]
   public void AroundOpeningConnection(JoinPoint jp)
   {
      timer = new Timer(_=> ((DbConnection)jp.Target).Close(), null, 0, TIMEOUT);
   }

   [Around("ClosingConnection")]
   public void AroundClosingConnection(JoinPoint jp)
   {
      timer.Dispose();
   }
}

Example: Actor Model

Ayende recently blogged about his Actor implementation. In this example, we’ll look at how to reimplement the solution using aspect-oriented approach. The idea is to enable actor capability on an arbitrary POCO object without modifying its codes.
The premise of an actor is that all non-read actions must be executed asynchronously (non-blocking), but these actions must be executed sequentially within each actor. Consider the following actions from Ayende’s example:

connection1.Send("abc");
connection1.Send("def");

I care for “abc” to be sent before “def”, and for both to be sent before anything else is sent through connection1. But I do NOT care if I have anything else sent between “abc” and “def” (through different connections).
Additionally, every read operation must be executed synchronously, while still maintaining the order of executions. (i.e. all read operations should execute after all previous queued actions have completed).

Since each actor object requires its own sequential tasks-queue, we need to associate an aspect with each actor instance. So let’s create an aspect with PerThis lifecycle to target our (synchronous) Connection class.

[AspectPerThis("NonReadActions")]
public class ActorAspect
{
   [SelectTypes("'Sheep.MyConnection'"]
   protected void TargetClass(){}

   [SelectMethods("Type:@TargetClass & !ReturnsVoid"]
   protected void ReadOperations(){}

   [SelectMethods("Type:@TargetClass & !@ReadOperations"]
   protected void NonReadActions(){}

   private readonly ConcurrentTaskQueue _queue = new ConcurrentTaskQueue();

   [Around("NonReadActions")]
   public void AroundActions(JoinPoint jp)
   {
      _queue.EnqueueToExecution(()=> jp.Execute());
   }

   [Around("ReadOperations")]
   public object AroundReading(JoinPoint jp)
   {
     using(_queue.EnterLock())
     {
         _queue.WaitTillCompleted();
         return jp.Execute();
     }
   }
}

Note: Similar to the previous example, this aspect should ideally be defined as an abstract aspect so we can reuse the actor pattern whenever it’s needed.

All the synchronous methods in our Connection class have now been fully converted to work synchronously, and follow the actor-model behavior that we desire. It’s all done without cluttering the actual Connection class with the complex plumbing of Actor pattern. We extract the pattern out into an aspect, leaving your original class clean from irrelevant bits of technical complexities.

Other Examples:

There are many other real world applications of PerThis and PerTarget lifecycles, including:

  • Dirty-tracking (tracks all changes of fields and properties within an object)
  • Lazy-Load proxy
  • Cache the output of a costly method that is called multiple times on the same object, e.g. fileStream.ReadToEnd()

Per-Control-Flow Lifecycle

If an aspect A is defined AspectPerFlow(Pointcut), then one object of type A is created for each flow of control of the join points picked out by Pointcut as the flow of control is entered. The advice defined in A may run ONLY at any join point within that control flow. An instance of the aspect is created upon entry into each such control flow.

Example: Context Passing

Consider you are developing a flight-booking application. As part of the booking process, the application will create flight itineraries objects.

var itinerary = new Itinerary(flight);

The constructor also automatically sets the CreatedDateTime property of the Itinerary object with the current system time.

Today you are requested to allow the system to be used by a travel-agent company who bulk their booking transactions in an end-of-day basis. So we provide them with the following client-facing API:

public void BookFlightFromTime(DateTime orderTime, Flight flight, Customer customer /* etc etc*/)
{
}

Note the first parameter “orderTime“. Being an end-of-day bulk operation, all Itinerary objects created within the scope of this method must have their CreatedDateTime properties assigned with the specified orderTime, rather than the current (end-of-day) system time.
Typically this requires passing the orderTime from the client, down through every method call that eventually leads to Itinerary creations. All programmers are familiar with the inconvenience of adding a method argument to a number of methods just to pass this kind of context information.

Using SheepAop, this kind of context passing can be implemented in a modular way. The following code adds an Around advice that runs when an Itinerary object is instantiated within the control-flow of BookFlightFromTime method.

[AspectPerFlow("BookFlightFromTimeMethod")]
public class OrderDateAspect
{
   [SelectMethod("void BookingClient::BookFlightFromTime(DateTime, *)")]
   protected void BookFlightFromTimeMethod(){}

   [SelectConstructor("InType: 'Sheep.Itinerary'")]
   protected void CreatingItenerary(){}

   private DateTime _orderTime;

   [Around("BookFlightFromTimeMethod")]
   public void BookFlightFromTimeMethod(MethodJoinPoint jp)
   {
      _orderTime = jp.Args[0];
   }

   [Around("CreatingItenerary")]
   public void AroundCreatingIntenerary(JoinPoint jp)
   {
      jp.CreatedDateTime = _orderTime;
   }
}

This aspect affects only a small number of methods, but note that the non-AOP implementation of this functionality might require editting many more methods, specifically, all the methods in the control-flow from the client to the Itenerary creation. This is one major characteristic of aspect-lifecycle control. While the aspect is short and affects only a modest number of benefits, the complexity the aspect saves is potentially much larger.

Example: Transactional Methods: The Right Way

Transaction is one of the most cliche example in aspect-oriented-programming examples, and I’ve promised I would not mention the word today. But this one is not your typical transaction AOP demo.
The typical implementations in most AOP demos are usually overly naive. To explain what I mean, let’s write a simple ordinary TransactionAspect (using default Singleton lifecycle)

[Aspect]
public class TransactionAspect
{
   [SelectMethods("'* Sheep.ApplicationService.*::*(*)' & Public")]
   private void TransactionalMethods(){}

   [Around("TransactionalMethods")]
   public object AroundTransactionalMethods(JoinPoint jp)
   {
      using(var tx = new Transaction())
      {
         var returnValue = jp.Execute();
         tx.Commit();
         return returnValue;
      }
   }
}

That was quite straightforward. But now consider if you have non-transactional operations within your methods, e.g. sending an email. You don’t want to send out the email if the transaction fails. Furthermore, if you apply automatic retries on your transaction aspect, you won’t want multiple duplicate emails sent out.
It’s usually hard to solve this problem using other AOP solutions that do not support any notion of lifecycle control.

We will modify our TransactionAspect above to intercept all calls to non-transactional methods, and defer their executions until at the end of the transaction (upon successful completion of the transaction).

[AspectPerFlow("TransactionalMethods")]
public class TransactionAspect
{
   [SelectMethods("'* Sheep.ApplicationService.*::*(*)' & Public")]
   private void TransactionalMethods(){}

   [SelectMethods("HasCustomAttributeType: 'Sheep.NonTransactionalAttribute' & ReturnsVoid")]
   private void NonTransactionalMethods(){}

   private Queue<Action> _nonTransactionalActions = new Queue<Action>();

   [Around("NonTransactionalMethods")]
   public void StealNonTransactionalMethods(JoinPoint jp)
   {
      _nonTransactionalActions.Add(()=> jp.Execute());
   }

   [Around("TransactionalMethods")]
   public object AroundTransactionalMethods(JoinPoint jp)
   {
      using(var tx = new Transaction())
      {
         var returnValue = jp.Execute();
         tx.Commit();

        foreach(var action in _nonTransactionalActions)
           action();
        return returnValue;
     }
   }
}

Now all executions of non-transactional methods will be intercepted, and stored to a queue to be executed later when the transaction is completed.
A non-AOP implementation of this solution is non-trivial. You would have to massively change the structure of your code to pull out all calls to non-transactional methods, move them to the end of the transaction, and somehow maintain the contexts that are required by those methods.

Summary

Aspect-lifecycle is a key feature in SheepAop that takes aspect-orientation from logging and transaction to a whole another class of far more complex real-world problems.
The ability to associate the state of an aspect to a specific program context allows rich yet elegant aspect-oriented solutions, often affecting only a modest number of areas, but would otherwise require potentally a significantly larger complexity, if possible at all, using conventional (singleton) AOP solutions.
Later in the series, we will explore how you can create your own aspect-lifecycle implementations, such as PerHttpSessionLifecycle, or PerHttpRequestLifecycle

SheepAop Road Map

People have asked me what my next plans are for the project. So here’s a quick list of some works I got lined up for SheepAop, ordered by priority:

  1. Documenting and posting blog articles.
    Writing has always been, and will forever be, astronomically tedious and majestically mind-numbing. There has been zero coding activity in SheepAop for a good couple of weeks now since I started the documentation work; and it’s still nowhere near finish.
    Writing is depressing, often suicidal. That’s a universal truth. But an open-source project is only as useful as how much people know how to use it. So this has to be done as my utmost priority before I embark on any further development effort.
  2. Looking into all those NDepend inspection warnings and refactor accordingly. (I’ll blog about the whole process).
  3. SAQL Query Analyzer.
    This would be a fantastic tool to get a visual understanding on SheepAop pointcuts and various SAQL expressions. It will help the learning curve, and ultimately the adoption of SheepAop. One small catch: I’m technically daft and aesthetically hopeless at GUI design.
  4. CFlow pointcuts. Ability to use the control-flows of the program as part of your pointcut expressions. (e.g.: The pointcut of a dirty-tracking aspect should not match property-settings called from within the control flow of the object constructor). Based on AspectJ feature with the same name.
  5. Working on a few pointcut types, SAQL criteria, and aspect-lifecycles that are currently unimplemented (most importantly: the Constructor and Instantiation pointcuts)
  6. Add features to help unit-testing
  7. Support “ImplementInterface” advice
  8. Support “Mixin” advice

Quite a fat pipeline ahead. It will keep me busy for at least the next couple of months.

SheepAop Code Review with NDepend (Part I)

Now that SheepAop has reached its first milestone, I think it’s time to stop for a moment and review the code to discover potential code-smells before I engage any further development.
I’ll use NDepend for our code-review exercise today, and I’ll be doing it live while I’m reviewing the code, refactor, and write it down on this post on every step as I go along.

Dependency Graph

clip_image002[4]

The very first thing I did was to check the dependency graph to get an overall understanding about the structure of the code. To my surprise, the dependencies did not immediately look quite as obvious as the picture I had in my head.

A large part of the graph still fits very well with what I expected:

  1. There are some helper-classes and exceptions down at the bottom, which contain general-purpose classes that help streamline our contact with the outside world (.net collections, mono-cecil, etc). These helpers are common and should be completely agnostic to anything SheepAop, which is confirmed by this graph: there’s no line coming out from these modules.
  2. SheepAop.Aspects contains a set of out-of-the-box sample implementations of SheepAop aspects. They’re not part of the framework, so I don’t expect to see anything SheepAop to reference it. The graph agrees.
  3. SAQL is a DSL layer on top of SheepAop API. The core machinery of SheepAop should be completely agnostic of SAQL. The graph looks fine: all Saql namespaces appear to be left alone by the rest of the town.
  4. SheepAop is designed so that making new Advice implementations should be easy because it’s the one part in SheepAop that will change (and be added) the most. For that reason, all advice classes should be external to the core so that it’s easy to plug new ones. The graph seems to agree with that. There are currently only 2 advices in SheepAop: AroundAdvice and LifecycleAdvice, and the graph shows no arrow pointing to that direction. (Except the unexpected arrow from Pointcuts to AroundAdvising, which we’ll explore below).

Potential Problems

There are, however, few things that I was not categorically pleased. I’ll list them all as a note for now, even though I’m not sure whether or not they turn out to be code-smells. But we will investigate each one of them later on.

  1. I was surprised to see how much SheepAop.Compile namespace assumes such a central role in the whole architecture. Its responsibility is supposed to be limited to the compilation part of SheepAop. It should depend on other SheepAop components to do this task, but conversely the other components should NOT have much interest in the compilation business. The graph, however, shows that almost every part of SheepAop depends on the compiler, which doesn’t seem right.
  2. As mentioned above (in point#4), SheepAop Advice classes should be pluggable modules that adding new ones should be cheap and easy. I’m quite curious about why SheepAop.Pointcuts namespace has any hard reference to AroundAdvice.
  3. I also did not expect to see SheepAop.Core to depend on the Runtime component and Attributes. Attributes is supposed to provide users with a friendly interface to interact with the core SheepAop API. What’re the attributes possibly doing that makes them so critical even the core engine has to depend on it?
  4. Like Advices, Lifecycles needs to be a pluggable thing so that it’s easy to add new ones (I’m still developing some as we speak), as well as for users to make their own custom Lifecycles to be plugged to SheepAop. I was hoping not to see any hard reference from the Core to Lifecycles if I can help it. We’ll see what we can do.
  5. (After the first refactoring below) We have cyclic dependencies between Core<->Pointcut. We’ll investigate this. Probably Pointcut should really be part of the Core, considering how central the role it plays in SheepAop.

Investigation

Let’s now investigate each of them. I’ll start from the first one.

1. Everything -> SheepAop.Compiler

There are currently several dependencies toward the compiler which we want to get rid of. I’ll start with the most worrying one: the circular dependency between the Compiler and the Core.

clip_image003[4]

The one I need to look out for is the reference INTO the Compiler (greens and blacks). The answer seems quite obvious here: AdviceBase and AspectDefinition are defined as Compiler components. They are not. They have little to do with compilation. Let’s try moving these classes into the Core namespace. If they are indeed part of the Compiler, there will appear even more dependencies toward the Compiler coming from these classes. I’ll also need to bring along IAdvice and IWeaver since they are depended upon. So let’s try and refactor this.

clip_image005[4]

Great! The dependencies are now heading to one direction. All the unit-tests still pass. So this one is sorted. Let’s take a look on our next dependency on the Compiler.

Wait, they’re all gone!

clip_image006[4]

No more circular dependency on the Compile components (orange). It turns out that those 4 classes we just refactored were the culprit of all the heavy dependencies on the Compiler. As shown by the green blocks, the only components that now depend on our Compiler are our 2 Advices: AroundAdvice and LifecycleAdvice. That sounds perfectly correct. And the picture is starting to take its shape.

2. Pointcuts -> AroundAdvising

We want all Advices (AroundAdvice in this case) to be decoupled from the framework because it’s one part in SheepAop that will change (and added) the most, so it has to be easy to plug new Advices.

Same deal, I click on the line between Pointcuts and AroundAdvising to view the matrix. (It’s quite a tall picture so I cut out the irrelevant bits).

clip_image008

The green squares are the ones that shouldn’t be there. I wonder what they are. Let’s check the first one: PropertyPointcut.

clip_image010

That’s a method I found in the class. That’s a leftover method I inherit from the old version of SheepAop infrastructure (when Advice was still tightly coupled to the system). I don’t think they still contribute to the new SheepAop infrastructure. I checked the other green squares and they all seem to have the same garbage leftovers. Let’s get rid of them and run the unit-tests to make sure it doesn’t break anything.

clip_image011

Great, all the greens are cleared. All unit-tests still pass.

clip_image013

Now AroundAdvice is completely decoupled from SheepAop. The graph (centered on AroundAdvising) shows no green, meaning that there’s nothing in the whole SheepAop that depends on it (except our public Attributes). So this has been another success.

3.A SheepAop.Core <-> SheepAop.Runtime

There are several components that SheepAop.Core is dependent on, which it shouldn’t. Let’s start with the Runtime namespace. SheepAop Core should not depend on its Runtime components.

clip_image015

The green ones should not be there. It seems that they are all pointing to 2 method delegates (AdviceInvoker and AdviceCallback at the top), all of which are coming from Joinpoint classes. It makes me feel that those 2 delegates are integral parts of SheepAop Joinpoint system. Maybe I should put them together with the Joinpoint. In fact, I start to feel that these objects warrant their own separate namespace ( “Joinpoint”, instead of piggybacking the “Core” namespace). But first thing first.

clip_image017

Great. Now let’s split all those Joinpoint classes to a separate namespace, and hope that the resulting namespaces won’t cause cyclic dependencies.

clip_image019

Wow, this is unexpected. Not only that Core and Joinpoint did not end up in a cyclic dependency, there is in fact no dependency at all between them! They are both unrelated. It really was, after all, a good decision to split them to separate namespaces. But now it makes me wonder about the name. What’s with the name “SheepAop.Core.Joinpoint” if it’s not related to the Core? Since Runtime is the biggest dependent of this namespace, I think I should merge it with Runtime. After all, a join-point is a SheepAop representation model to describe runtime snapshots. Ironically, it turns out that the 2 delegates we pulled out of Runtime namespace were actually at the right place after all.

clip_image021

OK, we’re good. Let’s move on.

3.B SheepAop.Core <-> SheepAop.Attributes

As explained at the start of this post, Attributes is a facade API for users to interact with SheepAop framework, and it should sit right at the edge, together with SAQL. So why does the Core depend on it?

image
Aha, a black one. SingletonAttribute extends LifecycleAttributeBase, but LifecycleAttributeBase has a static CurrentInstance that defaults to SingletonAttribute. A cyclic.

image

Generally I have no problem with this kind of cyclic dependency because it does not indicate a code smell. But in this particular case, LifecycleAttributeBase needs to be refactored anyway into an ILifecycleProvider interface which will fit better as a Core component. In general, attributes are intended to be used as user API. Doesn’t feel right to see it as a part of the Core engine. So yeah, I’ll refactor this, and hope that it will also solve the circular dependency.

image

It does. That was quite a big refactoring, but all the unit-tests still pass, so we’re good. We’re now on a much better state with this refactoring.
As shown by the green blocks, the only component that knows anything about SheepAop Attributes (orange) is now only the Compiler, which sounds fantastically correct. (SheepAop.Aspects, as mentioned earlier, is just a sample implementation of SheepAop. So just ignore it, it’s not part of the framework).

Notice that our refactoring has also cleared the cyclic dependency between the Core and Lifecycle, so we just unconsciously solved the first half of task#4.

4.A Sheep.Core.Lifecycle <-> Sheep.Core

(Done)

4.B Sheep.Aop.Lifecycle <-> SheepAop.Runtime

To reiterate, we want Lifecycle to be decoupled from the framework so that it’s easy to introduce new ones (which are being developed as we speak), and for users to add their custom ones.

image

Ah this one is easy. IMayHaveAspect is a very specific spare-part of the PerThis lifecycle, so it should really belong in the Lifecycle namespace. So let’s move it.

image

Ah looks much better now.

5. SheepAop.Core <-> SheepAop.Pointcuts

I have a strong feeling that Pointcut should be merged into the Core. But let’s see.

image

Ah it looks like the Core only knows Pointcut by IPointcut interface and PointcutBase. I think I can move IPointcut to be part of the Core, and refactor the reference to PointcutBase to use the interface instead. (It’s generally a good idea to program against interfaces anyway).

image

Sweet. The cyclic traffic is gone, and the dependency becomes unidirectional: SheepAop.Pointcut –> SheepAop.Core, which feels quite right. So this is our final result:

image

Now we can see no more direct cyclic dependency in our code. But I can’t convincingly tell whether there is any indirect circular dependencies in the graph.
The graph is messy. Now that the relationships between the components have changed substantially, I think we should rearrange our graph to better reflect the new shape of our architecture. (I’ll also hide helper classes and exceptions from the graph).

image

The modules are now arranged so that all dependencies flow from top to bottom. The structure of the architecture is now very visible from the graph. As you can see, all arrows are pointing downwards. Or in simple terms: there is no possible circular dependency between our modules, directly or indirectly.

Next Step

I think that’s it for today, I’m calling it a night. We have achieved quite a good result so far. Tomorrow (or whenever I got time), I will continue the review with NDepend Metrics and CQL code-inspections. I can see some warnings already on my NDepend panels indicating some problems detected by NDepend code-inspector. But I’m too tired for the day, so I’ll spare that for the next post.

All refactored code has been checked back in to the SheepAop repository. You can check it out if you want to explore the result of today’s refactoring.

SheepAOP Part 2 – Pointcut and SAQL Basics

This Series

  1. Getting Started with SheepAOP
  2. Pointcuts and SAQL Basics
  3. Aspects Lifecycles & Instantiations
  4. Integrating with IoC containers
  5. Aspects Inheritance & Polymorphism
  6. Attributive Aspects (ala PostSharp)
  7. Unit-testing your aspects

This post is based on SheepAop Preview 1.1.

This Post

This post will serve primarily more as a reference material than a blog post or a bed-time reading consumption. In fact, a substantial portion of this post will quickly find its way to the SheepAop project documentation/wiki pages any time soon.

Pointcuts

We have covered this one quite briefly in the previous post.
Pointcut is an aspect-oriented mechanism to pick out join-points (e.g. method calls, properties, field access, instantiations) from your program code using some sort of query language.

The following is an example of how we define a pointcut to pick out all methods in Sheep.EmailService class:

[Aspect]
public class MyAspect
{
   [SelectMethods("Public & InType: 'Sheep.EmailService'")]
   public void MyPointcut(){}
}

Member Pointcut vs Call Pointcut

There are several types of pointcut in SheepAop, but they all generally fall into 2 categories: member-pointcut and call-pointcut.

Member-pointcuts (aka “Execution” pointcuts in AspectJ) pick out the structural elements of your program. For example your actual classes, methods, properties, fields, etc.
In the other hand, call-pointcuts (likewise “Call” pointcuts in AspectJ) pick out specific lines of instruction within the body of your program code.

An easy way to understand the difference is to take method-pointcut (a member pointcut) and a call-method-pointcut (a method pointcut) as an example.
When you apply an advice to a method-pointcut, you are weaving around the body of the targetted metod itself.
Whereas with a call-method-pointcut, you will be weaving every specific line in your code body that is found to be making a call to the targeted method.

Member Pointcuts

The following are the types of Member Pointcuts available in SheepAop:

Type Attribute Advices**
Types [SelectTypes(“saql”)] Mixin*
Methods [SelectMethods(“saql”)] Around
Properties (gets + sets) [SelectProperties(“saql”)] Around
Property-Gets [SelectPropertyGets(“saql”)] Around
Property-Sets [SelectPropertySets(“saql”)] Around
Fields [SelectFields(“saql”)] (none)
Constructors* [SelectConstructors(“saql”)] Around
Events* [SelectEvents(“saql”)] (none)

*) Not supported yet in the current release version of SheepAop
**) advices that are currently supported. The number might grow in the future

Call Pointcuts

In contrast to Member Pointcuts, which pick out structural element of your program, Call Pointcuts pick out specific line of instruction from within your program code.

There are different kinds of Call Pointcut recognizable by SheepAOP:

Type Attribute Advices**
Call Methods [SelectCallMethods(“saql”)] Around
Get Properties* [SelectGetProperties(“saql”)] Around
Set Properties* [SelectSetProperties(“saql”)] Around
Get Fields [SelectGetFields(“saql”)] Around
Set Fields [SelectSetFields(“saql”)] Around
Instantiations* [SelectInstantiations(“saql”)] Around
Add events* [SelectAddEvents(“saql”)] Around
Remove events* [SelectRemoveEvents(“saql”)] Around
Invoke events* [SelectInvokeEvents(“saql”)] Around

*) Not supported yet in the current release version of SheepAop
**) Advices that are currently supported. There might be more introduced in the future

SAQL

All pointcuts in SheepAop are expressed using a language called SAQL. Generally, there are 2 different types of SAQL syntax: literal-expression, and criteria-expression.

  • Literal Expression
    This is the simpler form of SAQL syntax, which looks and feels a lot like the pointcut language in AspectJ. I.e., it uses mainly member signatures and wildcards.
    Syntax: a single-quoted string

    'expression'

    Here are few examples:

    [SelectMethods("'System.Int32 System.String::IndexOf(System.Char, System.Int32)'")]
    [SelectMethods("'* System.*::???Of*(*, System.*)'")]
    [SelectTypes("'Sheep*.Domain.*.???Customer'")]
    

    See below for the literal format for each type of pointcut.
    Note: further development is underway to enhance the current SAQL literal-expression to follow a smarter DSL syntax, rather than the current implementation using the raw power of  wildcard string comparison.

  • Criteria ExpressionThis is a richer way to do complex queries that are not possible using simple AspectJ syntax. It allows us to describe conditions based on certain criteria-set supported by each type of pointcuts.
    Syntax:

    criteria

    … for unary criteria (taking no argument, e.g. Public, Static, HasGetter), or…

    criteria: argument

    … for binary criteria (taking an argument, e.g. Name:’*Repository’), or..
    Some examples:

    [SelectMethods("'Public & Name:'???CustomersBy*' & InType:Implements:Name:'*Repository'")]
    [SelectFields("(!Static & 'System.Int32 Sheep.*::_count') | (Static & Name: '*Count')")]
    [SelectProperties("((Static & Public) || (!Public & Virtual) & InType: (Implements:'Sheep.IRepository' | Inherits: 'Sheep.DataContext')")]
    

    Each type of pointcut has different set of supported criteria, which are all covered below.

Literal and Criteria expressions are not mutually exclusive. In fact, it is very common to mix both syntices within the same SAQL statement. For instance, take a look at lines #2 and #3 of the last example on Criteria Expression above. There are several literal-expressions embedded within the criteria (which are: ‘System.Int32 Sheep.*::_count’, ‘Sheep.IRepository’, and ‘Sheep.DataContext’ respectively).

Type Pointcut

Type-Pointcut Literal Format

In general, all literal-formats in SheepAop simply follows the standard qualified naming format in .Net framework. It hardly takes an inspired guess to work out the literal-format for ShepAop Type pointcut, which simply follows the naming format of .Net types.
Examples:

  • ‘System.Collections.Generics.IList`1’
  • ‘Sheep.OuterClass+NestedType’
  • ‘System.String[]’

Likewise any other literal expression, you can use ‘*’ and ‘?’ wildcards to match any string and any character respectively.

Type Pointcut Criteria

Criteria Argument Examples
Name string
  • Name: ‘*Customer’
  • Name: (‘*Service’ | ‘*Repository’)
Namespace string
  • !Namespace: ‘System.*’
ImplementsType Type Pointcut
  • ImplementsType:’System.Collections.IEnumerable’
  • ImplementsType: (Name: ‘*Repository’ | Namespace: ‘*.DataContexts’)
AssignableToType Type Pointcut
  • AssignableToType:(‘System.Collections.*’ & Interface)
  • AssignableToType: Namespace: ‘System.Collections.*’
HasMethod Method Pointcut
  • HasMethod: Name: ‘Get*’
  • HasMethod: (Public & Args(‘System.Int32’)
HasProperty Property Pointcut
  • HasProperty:Name:’Length’
  • HasProperty:Type:Implements:’*.*Service’
HasField Field Pointcut
  • HasField:Name:(‘_createdDate’ | ‘_entryDate’)
  • HasField:((Public & Static) | Protected)
ThisAspect (none)
  • ThisAspect
  • Implements:ThisAspect
  • Namespace:’Sheep.*’ & !ThisAspect
HasCustomAttributeType Type Pointcut
  • HasCustomAttributeType:ImplementsType:’BindableAttribute’
InheritsType* Type Pointcut
  • InheritsType:Namespace:’System.Collections’
Interface* (none)
  • Interface
  • !Interface
Abstract* (none)
  • Abstract & Name:’*Strategy’
ValueType* (none)
  • ValueType & HasMethod:Name:’Equals’
Class* (none)
  • Class & Implements:’Sheep.Irepository’

Tips: The names of SheepAop criteria follow a convention designed to describe the type of their arguments. E.g.: ImplementsType (takes a Type argument), HasMethod (takes a Method argument). This is to aid readability, and ultimately to prevent SAQL from becoming a write-only language like Regex.

Method Pointcut

Method-Pointcut Literal Format

Likewise Type-Pointcut, the literal-format for Method Pointcut simply follows the standard fully-qualified naming format of .net methods.
Examples:

  • ‘System.Int32 System.String::IndexOf(System.Char, System.Int32)’
  • ‘Sheep.OuterClass`1+NestedType::SomeMethod(System.String, System.Boolean[])’
  • ‘System.Int32 System.Object[]::SetValue()’

And of course, you can use ‘*’ and ‘?’ wildcards.

Method Pointcut Criteria

Criteria Argument Examples
Name string
  • Name:(‘Get*’ | ‘List*’)
Args
(Compare the first N arguments, not the entirety)
TypePointcut[…]
  • Args: (‘System.Int*’,  *, ‘System.String’)
  • Public & Args:(*, *, (Namespace:’Sheep.Service’ | Name:’*Service’))
InType Type Pointcut
  • InType:’System.String’ & Name:’IndexOf’
  • Public & InType:!ThisAspect
ReturnType Type Pointcut
  • Name:(‘IndexOf’|’LastIndexOf’) & ReturnType: ‘System.Int*’
  • ReturnType:((Interface | Abstract) & Name:’*Visitor’)
ReturnsVoid (none)
  • ReturnsVoid
  • ReturnType:’System.Boolean’ | ReturnsVoid
Static (none)
  • !Static
Virtual (none)
  • Virtual | Public
Public (none)
  • Public & Name: ‘Get*’
Private (none)
  • Private & InType:Implements:’System.Collections.IList’
Protected (none)
  • Protected & !Static
Internal (none)
  • Internal | Protected
Abstract* (none)
  • Virtual | Abstract
Implements* Method Pointcut
  • Private & Implements:’System.Collection.Ienumerable::GetEnumerator()’
HasCustomAttributeType Type Pointcut
  • HasCustomAttributeType:ImplementsType:’ValidationAttribute’

Property Pointcut

Property-Pointcut Literal Format

The literal-format of a Property Pointcut is exactly the same as that of a Method Pointcut.
Examples:

  • ‘System.Int32 System.Collections.IList`1::Count()’
  • ‘Sheep.OuterClass`1+NestedType::SomeProperty()’
  • ‘System.Int32 System.Object[]::Length()’

Yep, you guessed it, you can still use your good old ‘*’ and ‘?’ wildcards.

Property Pointcut Criteria

Criteria Argument Examples
Name string
  • Public & InType:’System.Collections.List’ & Name: ‘Count’
Type Type Pointcut
  • Name:’IsEmpty’ & Type:’System.Boolean’
InType Type Pointcut
  • Name: ‘Length’ & InType: ‘System.Array’
Static (none)
  • Static & InType:’System.DateTime’ & Name:’Now’
Virtual (none)
  • Virtual | Public
Public (none)
  • Public & Name: ‘*Count’
Private (none)
  • Private & InType:Implements:’System.Collections.IList’
Protected (none)
  • Protected & !Static
Internal (none)
  • Internal | Protected
HasCustomAttributeType Type Pointcut
  • HasCustomAttributeType:Name:’Required’
Abstract* (none)
  • Virtual | Abstract

Property Gets and Property Sets Pointcuts

Exactly the same as Property Pointcut, except that these pointcuts will pick out only the property getters and property setters respectively.

Field Pointcut

Field-Pointcut Literal Format

The literal-format of a Field Pointcut is quite similar to that of a Property Pointcut, minus the brackets.
Examples:

  • ‘System.Int32 System.Collections.IList`1::_items’
  • ‘Sheep.Web.ModelBinder::Instance’
  • ‘Sheep.Product::_price’

Same deal, ‘*’ and ‘?’ wildcards are your friends.

Field-Pointcut Criteria

Criteria Argument Examples
Name string Static & Public & Name: ‘Instance’
Type Type Pointcut Name:’_hasValues’ & Type:’System.Boolean’
InType Type Pointcut Name: ‘_items’ & InType: ‘System.Collections.Dictionary’
Static (none) !Static & Private
Virtual (none) Virtual | Public
Public (none) !Public & Type:(‘System.Boolean’|’System.Int16’)
Private (none) Private & InType:Implements:’System.Collections.IList’
Protected (none) Protected & !Static
Internal (none) Internal | Protected
HasCustomAttributeType Type Pointcut HasCustomAttributeType:Name:’Required’

Constructor Pointcut

(Not implemented yet in the current version of SheepAop, but will be very similar to Method Pointcuts in almost every respect).

About Call Pointcuts

Ok, time for a good tip. All call-pointcuts basically look the same. Each call-pointcut has a set of criteria “FromMethod”/”FromProperty” etc that represents the method where the line of call-instruction is found in its body. The other criteria represents what’s being called (e.g. a method, a field, a constructor, etc).

Literal expression is not available in any call pointcut by itself. But you can still use literal-expression to pick out the element of the call. For instance, the following pointcut …

SelectCallMethods[("Method: 'System.Int32 System.String::IndexOf(*)' & FromMethod:Type:'Sheep.StringHelper'")]
public void StringHelper_Calling_StringIndexOf(){}

… will pick out any call to String.IndexOf(*) methods made from any method body within the StringHelper class.

Call Methods Pointcut

Criteria

Criteria Argument Examples
Method string Method: (Static & Name: ‘Parse’ & Args(‘System.String’))
FromMethod Method Pointcut FromMethod:(Public & InType:Implements:’Irepository`1′)
FromPropertyGet* Property Pointcut FromPropertyGet: (Name: ‘TotalPrice’ & InType:’ShoppingCart’)
FromPropertySet* Property Pointcut FromPropertySet: ‘Sheep.IEntity::IsDeleted()’
FromConstructor* Constructor Pointcut FromConstructor: Type: ‘Sheep.*Service’

Get Fields Pointcut

Criteria

Criteria Argument Examples
Field string Field: (Static & Name: ‘Empty’ & InType: ‘System.Guid’)
FromMethod Method Pointcut FromMethod:(Public & InType:Implements:’Irepository`1′)
FromPropertyGet* Property Pointcut FromPropertyGet: (Name: ‘TotalPrice’ & InType:’ShoppingCart’)
FromPropertySet* Property Pointcut FromPropertySet: ‘Sheep.IEntity::IsDeleted()’
FromConstructor* Constructor Pointcut FromConstructor: Type: ‘Sheep.*Service’

Set Fields Pointcut

Criteria

(Exacly the same as Set Fields Pointcut, except that it will pick out the act of setting a field, not getting one).

Get-Properties Pointcut

(Not implemented yet in the current release of SheepAop, although it’s quite trivial)

Criteria

Criteria Argument Examples
Property string Property: (Static & Name: ‘Now’ & InType: ‘System.DateTime’)
FromMethod Method Pointcut FromMethod:(Public & InType:Implements:’Irepository`1′)
FromPropertyGet* Property Pointcut FromPropertyGet: (Name: ‘TotalPrice’ & InType:’ShoppingCart’)
FromPropertySet* Property Pointcut FromPropertySet: ‘Sheep.IEntity::IsDeleted()’
FromConstructor* Constructor Pointcut FromConstructor: Type: ‘Sheep.*Service’

Set Properties Pointcut

Criteria

(Exacly the same as Set Properties Pointcut, except that it will pick out the act of getting a property, not setting one).

Instantiation Pointcut

(Not implemented yet in the current release of SheepAop)

Criteria

Criteria Argument Examples
Constructor string Constructor:(Type: ‘String.DateTime’ & Args(‘System.Int32’, ‘System.Int32’)
FromMethod Method Pointcut FromMethod:(Public & InType:Implements:’Irepository`1′)
FromPropertyGet* Property Pointcut FromPropertyGet: (Name: ‘TotalPrice’ & InType:’ShoppingCart’)
FromPropertySet* Property Pointcut FromPropertySet: ‘Sheep.IEntity::IsDeleted()’
FromConstructor* Constructor Pointcut FromConstructor: Type: ‘Sheep.*Service’

And/Or

You can apply And and Or operations on literal-expressions, criteria, and the mix of them both. You use ‘&’ and ‘|’ operators, or alternatively ‘&&’ and ‘||’, which make no difference whatsoever apart from to satisfy your geeky obsession.
We have used And and Or quite a bit in our exhaustive list of examples above. But another example doesn’t hurt. We’ll mix a combination of literal-expression and criteria:

[SelectType("'System.Collections.ArrayList' | 'System.Array' | (ImplementsType: 'System.Collections.IEnumerable' & !Abstract & Namespace:'Sheep')")]
public void MyPointcut(){}

Negation

The operator you’re looking for here is ‘!’. Examples:

[SelectTypes("!Abstract")] // Self explanatory
[SelectTypes("!ImplementsType:'System.Collections.IEnumerable'")] // Does not implement IEnumerable
[SelectTypes("ImplementsType:!'System.Collections.IEnumerable'")] // Implements any interface that is not IEnumerable

Reference

A pointcut can be built out of other pointcuts. You can reference another pointcut using its name by using an operator similar to Razor’s: @pointcutName. For example:

[SelectMethods("Public & Name:('Get*'| 'List*') & InType:@Repositories")]
public void RepositoryQueriesPointcut(){}

[SelectMethods("Public & Name:('Update*') & InType:@Repositories")]
public void RepositoryUpdatesPointcut(){}

[SelectTypes("ImplementsType:'Sheep.Data.IRepository`1'")]
[SelectTypes("InheritsType:'Sheep.Data.DataService'")]
public void Repositories(){}

Array

Certain criteria requires multiple values, such as the Args criteria of Method Pointcut. You define array by using a ‘,’ operator. For instance:

[SelectMethods("Name: 'IndexOf' & Args: 'System.Char', 'System.Int32'")] //You can also give it a bracket if it makes you feel happier inside

Group

Nothing special, just grouping stuff together using parentheses operators to give precedence to a certain clause within an And/Or, Negation or Criteria statement. See below for the default order of precedence. Before that

[SelectMethods(
        @"Name: ('Count*'|'*Length')
        & InType:(!Abstract & Implements:(Name:'*Service' & Namespace:'Sheep.Domain'))
        & ReturnType: ('System.Int32' | 'System.Decimal')")]
public void CountMethods(){}

Order of Precedence

The order of precedence in SAQL operators follows the standard precedence in common programming languages. For completeness sake, the order is as follows, from the highest precedence to the lowest:

  1. ‘literal value’
  2. @pointcutRef
  3. (parentheses)
  4. criteria: argument
  5. !negation
  6. array, array, array
  7. and & or |

Summary

This post has documented the basic mechanic of SheepAop pointcut model and various SAQL syntax and criteria, which hopefully will come in handy as a reference manual to help you in writing your first SAQL queries. I plan to make a standalone SAQL Query-Analyzer tool that will allow you to instantly type in any SAQL query and execute it to immediately view the resulting join-points it picks out from your target assemblies. That would be a fabulous learning ground to try out various SAQL syntax and check the results immediately.

The next post will discuss about the practical use of SheepAop Aspects Lifecycle, where I hope we’ll finally get a chance to explore some practical examples on aspect-oriented-programming, using Aspect Lifecycle feature in particular.


*) Not supported yet in the current release version of SheepAop

SheepAOP Part 1 – Getting Started

SheepAOP released its first preview yesterday. It’s by no mean anywhere near complete, but I just wanted to put it out there so people could start playing with it, and try out whatever basic functionality there is. Throughout this blog post series, I will be exploring various bits and pieces of those functionalities.

This post is based on the current build of SheepAOP Preview-Release 1 Preview Release 1.1

This Series

This is what I currently have in mind about things that I plan to cover in the next several posts:

  1. Getting Started with SheepAOP
  2. Pointcuts and SAQL Basics
  3. Aspects Lifecycles & Instantiations
  4. Integrating with IoC containers
  5. Aspects Inheritance & Polymorphism
  6. Attributive Aspects (ala PostSharp)
  7. Unit-testing your aspects
  8. Extending SheepAop

I’m going to skip the whole boring introduction speech on Aspect Orientation, and will assume you have at least some basic prior knowledge of AOP concepts and technologies. If you’re not familiar with AOP, there are countless articles around AspectJ in the information-superhighway that is the Internet, which I highly recommend as a fantastic resource to learn about the concept of AOP. Now let’s get started on SheepAOP.

Your First Aspect

In SheepAOP, an aspect is just a normal POCO class that you decorate with Aspect attribute. For example:

[Aspect]
public class TransactionAspect
{
}

Idioms

SheepAOP uses the same formal AOP idioms as it was coined over a decade ago, and popularized by the de-facto AOP framework of all time: AspectJ. The critical element in this AOP design is the join-point model.

A join-point is a well-defined point in the program flow. There are many different types of them, encompassing the actions of calling a method, instantiating a class, setting/getting a property, accessing/assigning a field, etc. On most other AOP frameworks for .net out there, such as PostSharp, you typically target these join-points by placing marker attributes around these join-points (e.g. methods/properties/fields/classes), so the framework will pick them up and weave with croscutting behaviors.

SheepAOP, in contrast, uses the well-known AOP mechanic to target your join-points. This mechanic is formally known as pointcut.

Pointcuts

Pointcut is an aspect-oriented-programming mechanic acting as a query-expression to pick out certain join-points in your program flow.

Simple Pointcut

The following is a SheepAop example to pick out each join-point that is an execution of a method that has the signature void MyNamespace.MyService.GetCustomer(int):

[SelectMethods("'void MyNamespace.MyService::GetCustomer(System.Int32)'"]
public void MyPointcut(){}

The language you use to define pointcuts in SheepAOP is called SAQL (SheepAop Query Language), which will be covered in depth in the next post of the series.

More Complex Criteria

You can define vastly expressive pointcut queries using complex conditions. For example, the following pointcut will pick out “all public ‘GetXxx’ and ‘ListXxx’ methods within all classes that implement IRepository<T>“.

[SelectMethods("Public & Name:('Get*'| 'List*') & InType:Implements:'Sheep.Data.IRepository`1'")]
public void RepositoryQueriesPointcut(){}

That was a quick glimpse of SAQL that provides you an immensely rich pointcut model for expressing join-point queries that AspectJ can’t.

Pointcut Composition

A SheepAOP pointcut can contain a group of multiple query-expressions. It’s done by simply defining multiple Select Attributes, for example:

[SelectMethods("Public & Name:('Get*'| 'List*') & InType:Implements:'Sheep.Data.IRepository`1'")]
[SelectPropertyGet("Public & InType:Implements:'Sheep.Data.IRepository`1")]
public void RepositoryQueriesPointcut(){}

Note: Pointcut composition is not available yet in the current preview-release of SheepAop. Very simple to implement, but it’s a low priority feature. (Now supported)

Modularizing Pointcuts

A pointcut can be built out of other pointcuts, usually for modularity and reusability reasons. The SheepAop syntax to reference to another pointcut will look familiar to Razor users:  @pointcutName.
As an example, we will refactor the previous example, and additionally, we’ll also expand the pointcut to also pick out “all implementations of IDataService<T>” (in addition to IRepository<T>). And while we’re there, why not also add another pointcut to pick out all Update methods too.

[SelectMethods("Public & Name:('Get*'| 'List*') & InType:@Repositories")]
[SelectPropertyGet("Public & InType:Implements:@Repositories")]
public void RepositoryQueriesPointcut(){}

[SelectMethods("Public & Name:('Update*') & InType:@Repositories")]
public void RepositoryUpdatesPointcut(){}

[SelectTypes("Implements:'Sheep.Data.IRepository`1'")]
[SelectTypes("Implements:'Sheep.Data.IDataService`1'")]
public void Repositories(){}

(Tips: The “Repositories” pointcut above can actually be written more succinctly as follows:)

[SelectTypes("Implements:('Sheep.Data.IRepository`1' | 'Sheep.Data.IDataService`1')")]
public void Repositories(){}

Advice

So pointcuts pick out join-points, but they don’t do anything apart from picking out join-points. To actually implement crosscutting behavior, we use advice. Advice brings together a pointcut (to pick out join-points) and a body of code (to run at each of those join-points).

SheepAOP has several different kinds of advice (and still growing), but the most common one you’ll often find yourself using is the Around advice, which is an advice that runs as a join-point is reached, and has explicit control over whether the program proceeds with the join point execution. For example, let’s put an advice to log around our Queries and Updates pointcuts in our previous example.

[Around("RepositoryQueriesPointcut", "RepositoryUpdatesPointcut")]
public object LogQueriesAndUpdates(MethodJoinPoints jp)
{
   Console.WriteLine("Entering method {0} on object {1} with args {2}",
   jp.Method, jp.This, jp.Args);
   try
   {
      object result = jp.Execute();
      Console.WriteLine("Exits normally with return-value {0}", result);
      return result;
   }
   catch(Exception e)
   {
      Console.Writeln("Exits with exception: {0}", e);
      throw;
   }
}

Joinpoint.Execute() is a callback to your original join-point execution. Calling this method will transfer your advice to proceed with the original execution of your join-point.
But it doesn’t have to stay exactly original. You can alter the elements of this callback, such as changing the method arguments, or even changing the target instance of the method itself (i.e. the ‘this’ of the method), as shown in the following example:

[Around("MyPointcut")]
public object MyAdvice(MethodJoinPoints jp)
{
   jp.Args[0] = "My Replaced Value"; // Replacing 1st argument
   jp.Args[1] = 10*(int)jp.Args[1]; // Multiplying 2nd argument by 10
   jp.This = _anotherRepositoryInstance; // Redirecting the method call to another instance

   return jp.Execute(); // Executing the join-points using altered arguments and instance
}

Beyond Logging

Within an Around advice, you can do more than intercepting calls. You can alter completely the whole flow of the execution. For instance, you can quite easily make all your repository-query methods to only return a lazy-execution proxies, and therefore will defer your actual join-points execution only when the proxy gets triggered. (I.e., similar to Future-Query in NHibernate 3.0).

[Around("RepositoryQueriesPointcut")]
public object DeferQueries(MethodJoinPoints jp)
{
   return ProxyGenerator.CreateProxy(
   baseType: jp.Method.ReturnType,
   initTarget: ()=> jp.Execute());
}

Now all your query methods within all your repositories will return a proxy of their return-values. For instance, in the following example..

var customer = customerRepository.GetById(id);
// some other code doing some other thing
// eventually, it executes customer.FirstName
txtFirstName.Text = customer.FirstName;

… the repository method on #1 will NOT actually execute, not until later on when we trigger the property (FirstName) of the customer-proxy object on line #4, which will then execute your actual customerRepository.GetById() method and retrieve your actual customer object to fulfill the proxy.

Note: Why do we need this if we already have NHibernate’s future-query? Well, in cases where you’re not using NHibernate, or even an RDBMS at all. For instance: web-service, remote calls, I/O read, or costly calculations: anything that you want to avoid executing until when it’s really needed.
SheepAop offers an elegant solution to defer (as well as to cache) these costly executions, without adding unnecessary crosscutting noise to your actual program code, even within static methods)

Putting It All Together

Just to recap our logging-aspect example, the following is the complete code for our aspect.

[Aspect]
public class LoggingAspect
{
   [SelectMethods("Public & Name:('Get*'| 'List*') & InType:@Repositories")]
   [SelectPropertyGet("Public & InType:Implements:@Repositories")]
   public void RepositoryQueriesPointcut(){}

   [SelectMethods("Public & Name:('Update*') & InType:@Repositories")]
   public void RepositoryUpdatesPointcut(){}

   [SelectTypes("Implements:'Sheep.Data.IRepository`1'")]
   [SelectTypes("Implements:'Sheep.Data.IDataService`1'")]
   public void Repositories(){}

   [Around("RepositoryQueriesPointcut", "RepositoryUpdatesPointcut")]
   public object LogQueriesAndUpdates(MethodJoinPoints jp)
   {
      Console.WriteLine("Entering method {0} on object {1} with args {2}",
      jp.Method, jp.This, jp.Args);
      try
      {
         object result = jp.Execute();
         Console.WriteLine("Exits normally with return-value {0}", result);
         return result;
      }
      catch(Exception e)
      {
         Console.Writeln("Exits with exception: {0}", e);
         throw;
      }
   }
}

Setting Up

To enable SheepAOP compiler to weave your code automatically during compilation time, you will simply need to hook a post-compilation task within your .csproj file. If download SheepAOP into your project using NuGet (coming soon), everything will be set up automatically for you. There’s no additional step required.

However, if you’re to configure your SheepAOP manually (which is the case with the current preview-release), you need to perform the following steps:

  1. Download SheepAOP binaries. This post is based on SheepAop Preview v0.1.1, downloadable at http://sheepaop.codeplex.com/releases/view/65923.
  2. Copy all the binary files into a local folder. I recommend to check them in as part of your project’s version-control system.
  3. Right-click on your project, then select ‘Unload Project’.
    image
  4. Right-click again on your (currently-unloaded) project and select ‘Edit xxx.csProj’.
    image
  5. Within your .csproj file, locate this line: “<Import Project=”$(MSBuildToolsPath)\Microsoft.CSharp.targets” />”. Right underneath, add the following lines:
    <PropertyGroup>
       <SheepAopLocation>$(MSBuildProjectDirectory)\Libs</SheepAopLocation>
    </PropertyGroup>
    <UsingTask TaskName="PostCompileWeaveTask" AssemblyFile="$(SheepAopLocation)\SheepAop.Tasks.dll" />
    <Target Name="AfterBuild">
       <PostCompileWeaveTask TargetFile="$(MSBuildProjectDirectory)\$(OutputPath)$(MSBuildProjectName).exe" SheepAopLocation="$(SheepAopLocation)" />
    </Target>
    

    Change the SheepAopLocation property accordingly depending on where you place your SheepAop binaries (on step#2). In this example, my SheepAop binaries is under Libs folder within my project.

  6. Now right-click your project and select ‘Reload Project’. SheepAop is now hooked to your project. Now every time you build your project, SheepAop will automatically execute a post-compilation task to weave your aspects into your assemblies.

Summary

We have touched some of the basic features of SheepAOP. We have explored some standard AOP mechanics such as Aspects, Pointcuts, Join-Points, and Advices, and how they work in SheepAOP. We have built our first simple example of a SheepAop aspect, and finally hook it with the post-compilation task of your project. In the next article, we will explore more about the basics of SAQL syntax, and various types of pointcuts that SheepAOP supports.