Share via


Aspect Oriented Programming using .NET

Some time back I wrote an article on AOP using .NET languages. It's a long post so bear with me. In case you are interested only about C# go down here

What is AOP

Aspect Oriented Programming or AOP is an interesting concept that can be applied to many of the programming problems we solve everyday. In our Visual Studio team system code we have a lot of web-services and remoting code that essentially does the following

 public void MyMethod(int parameter){    Trace.EnteredMethod("MyMethod", parameter);    SecurityCheck();    // Bunch of processing    Trace.ExitMethod("MyMethod");}

This is not just peculiar to our domain but is seen across different domains. In OO programming classes and methods are designed for performing specific operations and common/duplicate functionality are factored out into common classes. However, there are cross-cutting concerns that span accross all classes and methods, like logging and security checks. OOP only partially solves this problem by requiring users to define separate classes for logging and security checks and requiring each class/methods needing these services to call them. AOP targets and solves this problem elegantly.

AOP divides code into base-code (code for your functionality) and a new construct called aspect. Aspect encapsulates these cross-cutting concerns using the following concepts

  • join-points: The points in the structure of base-code where the cross-cutting functionality needs to execute. This is typically when specific methods are entered or exited or properties are accessed.
  • point-cut: A logical description of join-points using some specific syntax
  • advice: additional code like logging and security check that each of these methods need to perform

The most mature AOP language is probably AspectJ which adds AOP extensions to Java. However, for this blog, I'd stick to .NET languages like AspectDNG, Aspect# and C#.

Language support for AOP

AOP support has to be built in to the language and/or framework because it is based on method call interception. Whenever a methods is called the framework needs to provide a stub to call some other piece of code. Though .NET CLR has this capability, but it is intrusive as you need an object to extend from MarshalByRefObject  or ContextBoundObject to allow method-interception. This is a serious limitation because each class needs to be written so that it supports AOP. Many AOP languages or language-extensions get around this limitation by using various techniques. The techniques generally fall into two broad categories runtime or dynamic weaving, compile-time or static weaving.

Aspect# is AOP language which uses static compile time weaving. It uses its own proxy (and not CLR's proxy) called DynamicProxy. DynamicProxy is generated compile time and works differently while proxying interfaces (generates dynamic class and delegates method calls to the target of invocation) and proxying classes (generates stub class that inherits from the target).

Aspect# provides syntax to define point-cut and call method-interceptors or advice for them. It's done as follows 

 import YourCompany.CMS.ContentProviders in YourCompanyAssembly
import YourCompany.CMS.Aop.Interceptors

aspect SecurityAspect for RSSContentProvider
     include Mixins.SecurityResourceImpl in MyMixinsAssembly




          pointcut method(* MyMethod(*))<br>           advice(TracingInterceptor)<br>     end 
end
public class TracingInterceptor : IMethodInterceptor
{
   public object Invoke(IMethodInvocation invocation) 
   {
       // Trace using information from IMethodInvocation
       // like Method, MethodInvocationTarget
       return invocation.Proceed(); 
   }
}

The important bits are marked in bold. The first block is the point-cut in a Ruby like syntax which specifies all methods with the name MyMethod to be included in the join-points.  The second block is the interceptor (advice) TracingInterceptor. The TracingInterceptor is a class that has to implement the IMethodInterceptor. It can call invocation.Proceed to continue with the method invocation once it's done with the tracing. So whenever the MyMethod is called TracingInterceptor.Invoke gets called.

Other languages like AspectDNG (.NET based AOP language-extension) accomplishes this using something called IL weaving. In this the target or base-code is coded in any language that can be compiled into MSIL like C#, VB.NET, J#. So the target code can look like

 using System; public class MyClass {    public int ProcessString(String s, out string outStr) {        // ...      }}

There is no special code or any type of modification needed on the base-code as evident from above which is plain-vanilla C# code. The aspect code is written as follows which can also be C# code and needs some additional assembly reference and attribute decoration for AspectDNG to pick them up

 using DotNetGuru.AspectDNG.MetaAspects; using DotNetGuru.AspectDNG.Joinpoints; using System;public class AspectsSample{     [AroundCall(  "* MyClass::ProcessString(*)"  )]     publicstaticobject YourMethodCallInterceptor(JoinPoint jp)  {        Console.WriteLine("Code before calls to '.. MyClass.ProcessString(..)'");        object result = jp.Proceed();         Console.WriteLine("Code after calls to '.. MyClass.ProcessString(..)'");        return result;     }
}

Here point-cut is specified using attributes like AroundCall, AroundBody. Both the base-code and the aspect code are separately compiled into different assemblies using respective compilers like csc into Target.exe and aspect.dll. Then the aspectdng.exe tool can be used which uses reflection to reach to the attribute in the aspect code to weave call to them so that a new assembly called Target-weaved.exe is created. In target-weaved.exe AspectDNG directly puts in calls to the aspects around the target code by inserting/replacing IL code wherever required.

There are some AOP languages like Encase which apply the aspects at run time. These languages use AOP frameworks which reads in configuration files for point-cuts and at runtime generate proxy classes that intercept calls, allows advice of the aspect to execute first and then invokes the actual target. The benefit is that there is not edit-compile cycle but it faces performance issues.

AOP in C#

Till now we were talking about non-mainstream languages to get AOP done. However, by doing a bit extra work we can get the same functionality in C# as well. The limitation with CLR is that it allows method interception only when the classes containing the methods inherit from MarshalByRefObject  or ContextBoundObject. When a class inheriting from ContextBoundObject is activated, the .NET interceptor comes into play. It creates a trasparent-proxy and a real-proxy. The transparent-proxy gets called for all invocation of the target. The transparent proxy serializes the call stack and passes that on to the real-proxy. The real-proxy calls the first message sink which is an object implementing the IMessageSink interface. Its the duty of this first message sink to call the next until the final sink goes and calls the actual target. In this sink chaining we can insert objects which can execute our aspect advice.

Another limitation with C# is that there is no way in C# syntax to specify join-points. We will circumvent these two limitations by inheriting the target classes from ContextBoundObject. We'll use attributes on specific classes so that all methods and field-setters in them become included into the join-points.

 using System;// Include the aspect frameworkusing Abhinaba.Aspect.Security; 
 [ Security()] public class MyClass : ContextBoundObject {    public int ProcessString(String s, out string outStr) {        Console.WriteLine("Inside ProcessString");        outStr = s.ToUpper();        return outStr.Length;    }
}

Here Security is an attribute defined in our Abhinaba,Aspect.Security namespace which pulls in our support for AOP and includes the current class and all its methods in the join-points. The whole AOP framework looks as follows. All the important parts are marked in bold.

 using System;using System.Diagnostics;using System.Runtime.Remoting.Messaging; using System.Runtime.Remoting.Contexts; using System.Runtime.Remoting.Activation; namespace Abhinaba.Aspect.Security{    internal class SecurityAspect : IMessageSink {        internal SecurityAspect(IMessageSink next)        {            m_next = next;        }         private IMessageSink m_next;             #region IMessageSink implementation        public IMessageSink NextSink        {            get{return m_next;}
        }        publicIMessage SyncProcessMessage(IMessage msg)      {     Preprocess(msg);     IMessage returnMethod = <br>                              m_next.SyncProcessMessage(msg);     return returnMethod;      }         public IMessageCtrl AsyncProcessMessage(IMessage msg, 
                                  IMessageSink replySink)        {            throw new InvalidOperationException();        }        #endregion //IMessageSink implementation
        #region Helper methods        private void Preprocess(IMessage msg)        {            // We only want to process method calls            if (!(msg is IMethodMessage)) return;            IMethodMessage call = msg as IMethodMessage;            Type type = Type.GetType(call.TypeName);            string callStr = type.Name + "." + call.MethodName;            Console.WriteLine("Security validating : {0} for {1}",
                          callStr, Environment.UserName);        }        #endregion Helpers    }    public class SecurityProperty : IContextProperty,                                     IContributeObjectSink
    {        #region IContributeObjectSink implementation        public IMessageSink GetObjectSink(MarshalByRefObject o, 
                                            IMessageSink next)        {            returnnewSecurityAspect (next);         }        #endregion // IContributeObjectSink implementation        #region IContextProperty implementation        // Implement Name, Freeze, IsNewContextOK        #endregion //IContextProperty implementation    }    [AttributeUsage(AttributeTargets.Class)]    public class SecurityAttribute : ContextAttribute    {        public SecurityAttribute() : base("Security") { }        public override void GetPropertiesForNewContext(
                               IConstructionCallMessage ccm)        {           ccm.ContextProperties.Add( newSecurityProperty ());         }    }
} 

SecurityAttribute derives from ContextAttribute and MyClass derives from ContextBoundObject, due to this even before the ctor of the class is called the framework instantiates SecurityAttribute and calls the GetPropertiesForNewContext passing it a reference to IConstructionCallMessage. SecurityAttribute creates an instance of SecurityProperty and adds it to the context. This addition makes the framework call the various IContextProperty methods that SecurityProperty implements and then calls the ctor of MyClass.

After this the first time any MyClass method or variable is referenced it calls GetObjectSink method of SecurityProperty through its IContributeObjectSink interface. This method returns a newly created instance of SecurityAspect. Till this you can consider everything as initialization code and SecurityAspect implements our main functionality for AOP advice.

When the instance of SecurityAspect is created its constructor is passed a reference to next message sink so that all the sinks can be chained and called one after the other. After this SyncProcessMessage is called which is our main method interceptor and where all processing is done. After doing all processing like security verification the code calls the target method. Then it can refer to the return value and do post-processing. With this we have AOP implementation albeit some intrusive code as the target codes needs to be modified for AOP support.

Possibilities

AOP is a very generic programming method and can be used in a variety of situation. Some of them are as follows

Sample code

The sample solution (VS2005) including all sources are available here. It contains sources for two different aspects, one for security and one for tracing both applied on the same class. I have applied conditional compilation attribute to the tracing aspect so that on release build tracing gets disabled.

Comments

  • Anonymous
    January 23, 2006
    Perfomance of C# example (inheriting ContextBoundObject) will be horrible, tried that, unfortunately it's not a good solution :(

  • Anonymous
    January 23, 2006
    I absolutely agree with you on that. The C# solution is not acceptable for real-life application. However, in case you are using AOP for something like design-by-contract verification for a client application where the advice is called only on debug versions you can try this. But for logging, security using C# just doesn't scale....

  • Anonymous
    January 23, 2006
    I had some serious troubles figuring out the flow of code in heavily asynchronous network code. Since I am too lazy to write any extra lines I figured out the following method to add tracing to the interesting points, perhaps it could be made an attribute but I am not sure about the perf implications of that.
    Just drop Trcr.Trace() or Trcr.Trace("blah") around to use it.

    public class Trcr
    {
    public static void Trace()
    {
    #if (TRACE)
    System.Diagnostics.Trace.WriteLine(DateTime.Now.ToLongTimeString() + " " + new System.Diagnostics.StackFrame(1, false).GetMethod().Name + " < " + new System.Diagnostics.StackFrame(2, false).GetMethod().Name);
    #endif
    }

    public static void Trace(string text)
    {
    #if (TRACE)
    System.Diagnostics.Trace.WriteLine(DateTime.Now.ToLongTimeString() + " " + new System.Diagnostics.StackFrame(1, false).GetMethod().Name + ":" + text + " < " + new System.Diagnostics.StackFrame(2, false).GetMethod().Name);
    #endif
    }
    }

  • Anonymous
    January 27, 2006
    You may also want to look at DotSpect (http://dotspect.tigris.org/). It's an AOP tool for .NET that I had developed for my Master's Thesis. It's a recent introduction to the AOP tool space for .NET. Although it is in very early stages, it has some interesting ideas like:

    1. Language like AspectJ to describe aspects
    2. Advice can be written in any .NET supported language. Right now it supports (C#, VB.NET)
    3. Open-source, has an extensible architecture for adding new syntax, language support.

    Try it out, but beware! it's nascent-ware! :)

  • Anonymous
    January 27, 2006
    I would personally never use the remoting context method of interception unless I already had a need for remoting contexts for some other reason.

    "Till now we were talking about non-mainstream languages to get AOP done."

    This is completely false. All dynamic proxy methodologies can be done in C# ... what you are looking at is the config file for aspect# you can just as easily define those items in code, it is simply being pushed out to a config file instead.

    The concept of mixins should also be a very improtant mention here as dynamic proxies support it through agrgegation but it cannot be supported via remoting contexts. While interceptors are useful for some aspects many require full mixins.

    When will MS provide a dynamic proxy in the framework itself? There obviously already is one that is not exposed being used to generate transparent remoting proxies :).



  • Anonymous
    January 28, 2006
    Pavan you got a interesting tool there. However, the AOP tool that I used Aspect# also uses static IL weaving to get AOP

  • Anonymous
    January 28, 2006
    The comment has been removed

  • Anonymous
    January 30, 2006
    If it came down to it, I would be happy to donate mine just so there is one that exists :) I am in the process of starting up a new AOP based community for .NET http://aspective.net it should be up within the next week or so.

  • Anonymous
    February 01, 2006
    You're crossing out aop through inheriting from ContextBoundObject due to its performance. What's the most efficient aop framework?

  • Anonymous
    February 01, 2006
    Performance-wise any of the frameworks that use static weaving would be better. I have not done profiling so I don't have data to back me up. So you can skip contextBoundObject or Encase if you are concerned about perf. I'd suggest using AspectDNG or Aspect#. Aspect# has a better representation of point-cut though....

  • Anonymous
    March 01, 2006
    Sorry, I hate to write something up on this blog that is only remotely related to the topic, but I wanted to know the status and any other info about Greg Young's AOP based community for .NET at http://aspective.net.  I went there, but, it appears to be something else.  Please clarify as to the AOP community.  I am currently trying to learn AOP and am trying to find all resources for it that will be very helpful.

    Thank you.
    --Monico

  • Anonymous
    November 16, 2007
    I care a lot about the nice nifty features that takes a software from being just a good software to a

  • Anonymous
    November 18, 2007
    I care a lot about the nice nifty features that takes a software from being just a good software to a

  • Anonymous
    November 18, 2007
    PingBack from http://msdnrss.thecoderblogs.com/2007/11/19/the-wow-factor-in-software/

  • Anonymous
    November 19, 2007
    PingBack from http://www.bakdevelopment.com/wp/archives/15

  • Anonymous
    November 19, 2007
    I was preparing for a presentation on Aspect Oriented Programming and I started re-looking for the solutions

  • Anonymous
    November 19, 2007
    PingBack from http://msdnrss.thecoderblogs.com/2007/11/20/lack-of-aspect-oriented-programming-support-in-net/

  • Anonymous
    November 19, 2007
    PingBack from http://msdnrss.thecoderblogs.com/2007/11/20/lack-of-aspect-oriented-programming-support-in-net-2/

  • Anonymous
    January 18, 2008
    Hi it is one of the good article.

  • Anonymous
    January 18, 2008
    It would have been nice,if you would have explained the following points with example join-points,point-cut,advice

  • Anonymous
    May 09, 2008
    Good article. I have faced a problem while coding AOP. I have a generic container named StackEx, and this class inherits ContextBoundObject. When i build the project, no problem. But, when i run the project, i face an error, "Generic ContextBoundObjects are not suported" thrown in MS VS 2008. how can i do my job. Is there any other solution. I want to use .net framework.

  • Anonymous
    September 19, 2010
    Hi abhinaba. Thanks for the nice post. If you have just a minute i'd ask you to answer the following question. "SecurityAttribute" has a class as target. So, I suppose that each aspect is processed each time a client calls any method of the target class. Is it correct? But what if I'm going to target just a single method of the target type? Something like this: [AttributeUsage(AttributeTargets.Method)] public class SecurityAttribute : ContextAttribute {  // ... } public class ServerSample : ContextBoundObject {  [Security()]  public Int32 DoSomeStuff(String s, out String output)  {      // ...  } } I noticed that "interception" never raises, so, no pre and post-processing. What's wrong? Did i miss something? If you could spend some words about this "issue" I would appreciate it very much!