Archive

Compile-Time Weaving

There are basically three ways to modify the behavior of a .NET programs during the build phase:

Source-Level Weaving

You have to parse the source code, modify it and compile it using a standard compiler. The disadvantage is that you have to write a parser-renderer for each of the targeted languages. But you have the possibility to add constructions to the syntax.

Tools:

Writing your own compiler

You can start from scratch or use from an existing open-source compiler like gcc or Mono. The disadvantage is clearly that you have to write your own compiler or extend an existing... and who can afford it? But, at this price, you can do virtually what you want.

Tools:

Modifying the MSIL code

Let the standard compiler do its job, then read the compiled assembly, modify it and write it back. This is the approach of PostSharp.

Tools:

Companion Files

The idea here is to use external files (text files or XML files) to apply aspects on the base code. The weaving can be done at any time using any technique (Compile-Time Weaving or Runtime Weaving). Even if this approach is extremely flexible and reasonably easy for the weaver developer, it may be impractical, in some situations, not to have the possibility to apply aspects to a class in the same source file as this class.

A good example is taken from the Policy Injection application Block of the Enterprise Library:

<policies>
     <add name="Authorize and Audit">
       <matchingRules>
         <add type="Microsoft.Practices.EnterpriseLibrary.PolicyInjection.MatchingRules.NamespaceMatchingRule, Microsoft.Practices.EnterpriseLibrary.PolicyInjection" 
              name="Namespace Matching Rule" 
              match="PolicyInjectionQuickStart.BusinessLogic" 
              ignoreCase="false" />
       </matchingRules>
       <handlers>
         <add name="Logging Handler" 
              type="Microsoft.Practices.EnterpriseLibrary.PolicyInjection.CallHandlers.LogCallHandler, Microsoft.Practices.EnterpriseLibrary.PolicyInjection.CallHandlers" 
              logBehavior="BeforeAndAfter"
              beforeMessage="Before" 
              afterMessage="After" 
              includeParameterValues="true" 
              includeCallTime="true" 
              includeCallStack="false" 
              severity="Information">
           <categories>
             <add name="Audit" />
           </categories>
         </add>
       </handlers>
    </add>
 </policies>

XML companion files may be difficult to read by humans, but they can easily be generated by interactive configuration tool.s

PostSharp can be used as a base to develop such weavers, but the integration with the build process should be customized.

AOP for .NET

AOP on .NET

These pages are about Aspect-Oriented Programming (AOP) techniques for the .NET Framework. They help you understand what all is AOP and how AOP is implemented on .NET. It is PostSharp-centric, but not limited to PostSharp. We always try to refer to other existing AOP tools when relevant.

What is Aspect-Oriented Programming (AOP)?

Aspect-Oriented Programming (AOP), also named Aspect-Oriented Software Development (AOSD), is an approach to software development that goes further in the direction of separation of concerns. Separation of concerns is one of the most important rules in software development. It states that the same concern should be solved in a single unit of code. This is also called modularization. In procedural programming, the unit of code is the procedure (or function, or method). In object-oriented programming, the unit of code is the class.

Some concerns cannot be implemented successfully using a pure procedural or object-oriented programming. An example is code security. If you want to secure objects and methods, you have to modify the code of each method. That's why security is said a crosscutting concern, because it crosscuts the unit of modularization of the programming paradigm, in this case the class.

An aspect is a concern that cross-cuts many classes and/or methods.

So AOP is a technique that allows to address issues that cross-cuts objects. AOP is frequently used to implement caching, tracing, security or failure injections.

See also our Glossary for technical terms.

Example

Say we have a large set of business objects and some of their methods need to be secured. The current user is required to be in some roles in order to be given the permission to execute the method. Using the AOP programming model, you could, instead of modifying each method, develop an aspect and 'apply' it on methods of interest. In PostSharp Laos, the aspect could look like this:

public sealed class RequiresRoleAttribute : OnMethodBoundaryAspect
{
  string[] roles;
  public string[] Roles 
  { 
    get { return this.roles; }
    set { this.roles = value; }
  }

  public override void OnEntry( MethodExecutionEventArgs e )
  {
    ((ISecurable) e.Instance).RequireRoles( this.roles );
  } 
}

This defines a custom attribute that can be applied to any method. You can use wildcards to apply the aspect to a set of objects and methods:

[assembly: RequiresRole( 
      Roles=new string[] { "Delete" }, TargetMethods="Delete*" )]

There are of course a lot of AOP techniques to achieve the same result. The illustration above is only one of them.

Major AOP techniques in .NET

There are basically two approaches to change the behavior of a program in .NET (i.e. to weave a .NET program):

  • Compile-Time Weaving: the program is modified during the build process on the development machine, before deployment.
  • Runtime Weaving: the program is modified during its execution, after deployment.

Expressing the semantics

We know what we want: apply additional behaviors to existing code. Now we need to know how to express it. We have to express the following facts:

  • What behavior we want to add (advices),
  • Where we want to add it (specification of join points using pointcuts).

The following approaches are available when the .NET Framework is targeted:

  • Extending the Language : Create a new .NET language with new constructs.
  • Companion Files : Use external files, for instance XML files.
  • Custom Attributes : Use 'normal' .NET custom attributes to annotate the code.
  • Domain-Specific Languages: Use of a graphical designer to express the semantics, as well as IDE integration to simplify build and debugging experiences
  • Programmatic Tipping : All previous approaches were declarative. Another solution is to have normal .NET imperative code to add advices to join points.