December 2010

Volume 25 Number 12

Cutting Edge - Aspect-Oriented Programming, Interception and Unity 2.0

By Dino Esposito | December 2010

image: Dino EspositoThere’s no doubt that object orientation is a mainstream programming paradigm, one that excels when it comes to breaking a system down into components and describing processes through components. The object-oriented (OO) paradigm also excels when you deal with the business-specific concerns of a component. However, the OO paradigm isn’t as effective when it comes to dealing with cross-cutting concerns. In general, a cross-cutting concern is a concern that affects multiple components in a system.

To maximize the reuse of complex business logic code, you typically tend to design a hierarchy of classes around the core and primary business functions of the system. But what about other non-business-specific concerns that cross-cut through the hierarchy of classes? Where would you fit features such as caching, security and logging? Most likely, they end up being repeated in every affected object.

Not being a specific responsibility of a given component or family of components, a cross-cutting concern is an aspect of the system that must be dealt with at a different logical level, a level beyond application classes. For this reason, a distinct programming paradigm was defined years ago: aspect-oriented programming (AOP). Incidentally, the concept of AOP was developed at Xerox PARC laboratories in the 1990s. The team also developed the first (and still most popular) AOP language: AspectJ.

Even though almost everyone agrees on the benefits of AOP, it’s still not widely implemented. In my opinion, the main reason for such a limited adoption is essentially the lack of proper tools. I’m pretty sure the day that AOP is (even only partially) supported natively by the Microsoft .NET Framework will represent a watershed in the history of AOP. Today, you can only do AOP in .NET using ad hoc frameworks.

The most powerful tool for AOP in .NET is PostSharp, which you can find at sharpcrafters.com. PostSharp offers a full AOP framework where you can experience all the key features of the theory of AOP. However, it should be noted that many dependency injection (DI) frameworks include some AOP capabilities.

For example, you find AOP capabilities in Spring.NET, Castle Windsor and—of course—Microsoft Unity. For relatively simple scenarios, such as tracing, caching and decoration of components in the application tier, the capabilities of DI frameworks usually do the trick. However, it’s difficult to go with DI frameworks when it comes to domain objects and UI objects. A cross-cutting concern can certainly be seen as an external dependency, and DI techniques certainly allow you to inject external dependencies in a class.

The point is that DI will likely require ad hoc up-front design or a bit of refactoring. In other words, if you’re using a DI framework already, then it’s easy to bring in some AOP features. Conversely, if your system is DI-free, bringing in a DI framework may require quite a bit of work. This may not be always possible in a large project or during the update of a legacy system. With a classic AOP approach, instead, you wrap up any cross-cutting concerns in a new component called an aspect. In this article, I’ll first give you a quick overview of the aspect-oriented paradigm and then move on to see the AOP-related capabilities you find in Unity 2.0.

A Quick Guide to AOP

An object-oriented programming (OOP) project is made up of a number of source files, each implementing one or more classes. The project also includes classes representing cross-cutting concerns such as logging or caching. All classes are processed by a compiler and produce executable code. In AOP, an aspect is a reusable component that encapsulates the behavior required by multiple classes within the project. The way in which aspects are actually processed depends on the AOP technology you’re considering. In general, we can say that aspects aren’t simply and directly processed by the compiler. An additional technology-specific tool is required to modify the executable code to take aspects into account. Let’s briefly consider what happens with AspectJ—a Java AOP compiler that was the first AOP tool created.

With AspectJ, you use the Java programming language to write your classes and the AspectJ language to write aspects. AspectJ supports a custom syntax through which you indicate the expected behavior of the aspect. For example, a logging aspect might specify that it will log before and after a certain method is invoked. Aspects are in some way merged into the regular source code and produce an intermediate version of the source code that will then be compiled to an executable format. In AspectJ jargon, the component that preprocesses aspects and merges them with source code is known as the weaver. It produces an output that the compiler can render to an executable.

In summary, an aspect describes a reusable piece of code that you want to inject in existing classes without touching the source code of those classes. In other AOP frameworks (such as the .NET PostSharp framework), you won’t find a weaver tool. However, the content of an aspect is always processed by the framework and results in some form of code injection.

Note that in this regard, code injection is different from dependency injection. Code injection refers to the ability of an AOP framework to insert calls to public endpoints in the aspect at specific points within the body of classes decorated with a given aspect. The PostSharp framework, for example, lets you write aspects as .NET attributes that you then attach to methods in your classes. PostSharp attributes are processed by the PostSharp compiler (we could even call it the weaver) in a post-build step. The net effect is that your code is enhanced to include some of the code in the attributes. But the injection points are resolved automatically, and all you do as a developer is write a self-contained aspect component and attach it to a public class method. It’s easy to write and even easier to maintain the code.

To finish off this quick overview about AOP, let me present a few specific terms and clarify their intended meaning. A join point indicates a point in the source code of the target class where you want to inject the aspect’s code. A pointcutrepresents a collection of join points. An advice refers to the code to inject in the target class. The code can be injected before, after and around the join point. An advice is associated with a pointcut. These terms come from the original definition of AOP and may not be reflected literally in the particular AOP framework you’re using. It’s recommended that you try to pick up the concepts behind the terms—the pillars of AOP—and then use this knowledge to better understand the details of a particular framework.

A Quick Guide to Unity 2.0

Unity is an application block available as part of the Microsoft Enterprise Library project, as well as a separate download. The Microsoft Enterprise Library is a collection of application blocks that addresses a bunch of cross-cutting concerns that characterize .NET application development—logging, caching, cryptography, exception handling and more. The latest version of the Enterprise Library is 5.0, released in April 2010 and coming with full support for Visual Studio 2010 (learn more about it at the patterns & practices Developer Center at msdn.microsoft.com/library/ff632023).

Unity is one of the Enterprise Library application blocks. Also available for Silverlight, Unity is essentially a DI container with additional support for an interception mechanism through which you can make your classes a bit more aspect-oriented.

Interception in Unity 2.0

The core idea of interception in Unity is enabling developers to customize the chain of calls that it takes to invoke a method on an object. In other words, the Unity interception mechanism captures calls being made to configured objects and customizes the behavior of the target objects by adding some extra code before, after or around the regular execution of methods. Interception is essentially an extremely flexible approach to add new behavior to an object at run time without touching its source code and without affecting the behavior of classes in the same inheritance path. Unity interception is a way to implement the Decorator pattern, which is a popular design pattern devised to extend the functionality of an object at run time as the object is used. A decorator is a container object that receives (and maintains a reference to) an instance of the target object and augments its capabilities toward the outside world.

The Interception mechanism in Unity 2.0 supports both instance and type interception. Furthermore, interception works regardless of the way in which the object is instantiated, whether the object is created through the Unity container or is a known instance. In the latter case, you can just use a different, completely standalone API. If you do so, however, you lose the configuration file support. Figure 1 shows the architecture of the interception feature in Unity, detailing how it works on a particular object instance not resolved through the container. (The figure is just a slightly reworked version of a figure you find in the MSDN documentation.)

image: Object Interception at Work in Unity 2.0

Figure 1 Object Interception at Work in Unity 2.0

The interception subsystem is made of three key elements: the interceptor (or proxy); the behavior pipeline; and the behavior or aspect. At the two extremes of the subsystems you find the client application and the target object—that is, the object being assigned additional behaviors not hardcoded in its source code. Once the client application is configured to use the interception API of Unity on a given instance, any method invocation goes through a proxy object—the interceptor. This proxy object looks at the list of registered behaviors and invokes them through the internal pipeline. Each configured behavior is given a chance to run before or after the regular invocation of the object method. The proxy injects input data into the pipeline and receives any return value as initially generated by the target object and then further modified by behaviors.

Configuring Interception

The recommended way of using interception in Unity 2.0 is different from earlier versions, although the approach used in earlier versions is fully supported for backward compatibility. In Unity 2.0, interception is just a new extension you add to the container to describe how an object is actually resolved. Here’s the code you need if you want to configure interception via fluent code:

var container = new UnityContainer();
container.AddNewExtension<Interception>();

The container needs to find information about types to intercept and behaviors to add. This information can be added either using fluent code or via configuration. I find configuration to be particularly flexible, as it lets you modify things without touching the application and without any new compile step. Let’s go with the configuration-based approach.

To begin with, you add the following in the configuration file:

<sectionExtension type="Microsoft.Practices.Unity.InterceptionExtension.
  Configuration.InterceptionConfigurationExtension, 
  Microsoft.Practices.Unity.Interception.Configuration"/>

The purpose of this script is extending the configuration schema with new elements and aliases that are specific to the interception subsystem. Another due addition is the following:

<container> 
  <extension type="Interception" /> 
  <register type="IBankAccount" mapTo="BankAccount"> 
    <interceptor type="InterfaceInterceptor" /> 
    <interceptionBehavior type="TraceBehavior" /> 
  </register> 
</container>

To achieve the same thing using fluent code, you would call AddNewExtension<T> and RegisterType<T> on the container object.

Let’s look at the configuration script more closely. The <extension> element adds interception to the container. Note that the “Interception” being used in the script is one of the aliases defined in the section extension. The interface type IBankAccount is mapped to the concrete type BankAccount (this is the classic job of a DI container) and associated with a particular type of interceptor. Unity offers two main types of interceptors: instance interceptors and type interceptors. Next month, I’ll delve deeper into interceptors. For now, suffice it to say that an instance interceptor creates a proxy to filter incoming calls directed at the intercepted instance. Type interceptors, instead, just mock the type of the intercepted object and work on an instance of a derived type. (For more information on interceptors, see msdn.microsoft.com/library/ff660861(PandP.20).)

The interface interceptor is an instance interceptor limited to act as the proxy of only one interface on the object. The interface interceptor uses dynamic code generation to create the proxy class. The interception behavior element in the configuration indicates the external code you want to run around the intercepted object instance. The class TraceBehavior must be declaratively configured so the container can resolve it and any of its dependencies. You use the <register> element to tell the container about the TraceBehavior class and its expected constructor, as shown here:

<register type="TraceBehavior"> 
   <constructor> 
     <param name="source" dependencyName="interception" /> 
   </constructor> 
</register>

Figure 2shows an excerpt from the TraceBehavior class.

Figure 2 A Sample Unity Behavior

class TraceBehavior : IInterceptionBehavior, IDisposable
{
  private TraceSource source;
  public TraceBehavior(TraceSource source)
  {
    if (source == null) 
      throw new ArgumentNullException("source");
    this.source = source;
  }
   
  public IEnumerable<Type> GetRequiredInterfaces()
  {
    return Type.EmptyTypes;
  }
  public IMethodReturn Invoke(IMethodInvocation input, 
    GetNextInterceptionBehaviorDelegate getNext)
  {
     // BEFORE the target method execution 
     this.source.TraceInformation("Invoking {0}",
       input.MethodBase.ToString());
     // Yield to the next module in the pipeline
     var methodReturn = getNext().Invoke(input, getNext);
     // AFTER the target method execution 
     if (methodReturn.Exception == null)
     {
       this.source.TraceInformation("Successfully finished {0}",
         input.MethodBase.ToString());
     }
     else
     {
       this.source.TraceInformation(
         "Finished {0} with exception {1}: {2}",
         input.MethodBase.ToString(),
         methodReturn.Exception.GetType().Name,
         methodReturn.Exception.Message);
     }
     this.source.Flush();
     return methodReturn;
   }
   public bool WillExecute
   {
     get { return true; }
   }
   public void Dispose()
   {
     this.source.Close();
   }
 }

A behavior class implements IInterceptionBehavior, which basically consists of the Invoke method. The Invoke method contains the entire logic you want to use for any method under the control of the interceptor. If you want to do something before the target method is called, you do it at the beginning of the method. When you want to yield to the target object—or, more precisely, to the next behavior registered in the pipeline—you call the getNext delegate provided by the framework. Finally, you can use any code you like to post-process the target object. The Invoke method needs to return a reference to the next element in the pipeline; if null is returned, then the chain is interrupted and further behaviors will never be invoked.

Configuration Flexibility

Interception and, more generally, AOP, address a number of interesting scenarios. For example, interception allows you to add responsibilities to individual objects without modifying the entire class, keeping the solution much more flexible than it would be with a decorator.

This article just scratched the surface of AOP applied to .NET. In the next few months I’ll write more about interception in Unity and AOP in general.


Dino Esposito is the author of “Programming Microsoft ASP.NET MVC” from Microsoft Press (2010) and coauthor of “Microsoft .NET: Architecting Applications for the Enterprise” (Microsoft Press, 2008). Based in Italy, Esposito is a frequent speaker at industry events worldwide.

Thanks to the following technical expert for reviewing this article: Chris Tavares