Click here to Skip to main content
Click here to Skip to main content

The simplest AOP scenario in C#

, 29 Sep 2004
Rate this:
Please Sign up or sign in to vote.
A very simple library that you can use to implement not-so-simple constructs of Aspect Oriented Programming

Introduction

AOP is very interesting, because it addresses some common problems in complex programs. If you just don’t know what AOP is, I suggest you to read an article like [1].

Many different AOP implementations already exist, mainly as open source. Since I’ve seen many programmers, particularly in .NET world, play a little with the existing AOP libraries only to conclude that AOP is too complex to understand and insert in their projects, this article will introduce a simple approach to the base concepts of AOP, sketching the code necessary to build a custom AOP solution.

I don’t want to enter in the flame “AOP != Attributes + Interception” (see for example [2]), but I’m used to think about software fragments only in terms of their usefulness, not if they conform to some theory (ok, I just entered the flame! Smile | :) ).

What I want to say is that if everyone must agree that the theory of AOP is really rich and complex (and maybe beautyful because of this), very often what the programmer needs in every day working is something light and quick to handle, that can solve a great part of his/her problems.

I’ll talk about this and more all along the article, while commenting the code.

Interception and ContextBoundObject

Every AOP implementation is based, first of all, on some form of interception: it can be static, dynamic, precompiled, marked with attributes, and who knows how. What we know is that we have to intercept the calls to the methods of certain classes.

In .NET platform, this can be achieved mainly with two approaches: injecting the IL code with the desired redirections (precompiling the code with some instrument or modifying the result of a normal compilation), or basing these classes on the .NET library class ContextBoundObject. Handling bytecode is very difficult, not much evolution-resistant (what about the next release of .NET platform?), and available only to programmers with a lot of free time. Using ContextBoundObject as a base for AOP has been already investigated very deeply [3], and I can only add that is so easy that it’ll be our choice (but certainly limiting ourselves in the capabilities).

The most important concern is performance: while I suggest to read the documentation to understand why it is so slow to use ContextBoundObjects, let’s find some number. It will be very simple: just create a new project and insert for example the following code.

using System;
using System.Windows.Forms;

public class Test //: ContextBoundObject
{
 public void Go()
 {
  int k = 32;
  k *= 2;
 }

 public static void Start()
 {
  Test t = new Test();

  int starttime = Environment.TickCount;
  for (int i = 0; i < 5000000; i++)
   t.Go();
  int endtime = Environment.TickCount;

  MessageBox.Show("Total time is: " + (endtime - starttime).ToString());
 }
}
  

In my test machine executing Test.Start() gives on average a result of 31. Removing the comment I obtain on average 109. This is more than 300% slower, that can look as an incredible cost just to use a CBO. But we have also to think to absolute times: in the above test we made 5,000,000 invocations, that means that each invocation had a call time of 0,022 microseconds. In my opinion, this cost is totally acceptable for the best part of real-world applications: of course, adding services to the interception mechanism will have an impact on these times, and each time I will run again the tests to show you the results. Right now we can state one important thing: as you can see debugging the code over the line that builds an object of type Test, the reference t points to a TransparentProxy, that represents our chance to intercept the method call to the underlying object.

Interception

This part is the easiest: I just stole the code attached to the article [4], to which I must send my credits! This article is really interesting from the point of view of technology, but takes into account a lot of services that we don’t need at all. So, in the end, it’s not true that I just stole the code: I’ve thrown away what I don’t need here, and what remained is a very little bunch of code. I don’t want to explain again the details of the interception chain, and I will spend just a few words on the actors (the code for each is very simple, see the documentation for the details).

AOPAttribute – We need an attribute to mark the CBO, because we must add a dummy property to its context.

    [AttributeUsage(AttributeTargets.Class)]
    public class AOPAttribute : ContextAttribute
    {
        public AOPAttribute() : base("AOPAttribute") {}

        public override void GetPropertiesForNewContext(IConstructionCallMessage ctor)
        {
            if (AOPConfig.Enabled)
                ctor.ContextProperties.Add(new AOPProperty());
        }

        public override bool IsContextOK(Context ctx, 
            IConstructionCallMessage ctorMsg)
        {return false;}
    }

AOPProperty – The property is needed only to add a message sink.

    public class AOPProperty : IContextProperty, IContributeServerContextSink
    {
        public AOPProperty() {}

        public IMessageSink GetServerContextSink(IMessageSink nextSink)
        {
            IMessageSink logSink = new AOPSink(nextSink);
            return logSink;
        }

        public string Name {get {return "AOP";}}

        public bool IsNewContextOK(Context ctx)
        {
            AOPProperty newContextLogProperty = 
           ctx.GetProperty("AOP") as AOPProperty;
            if (newContextLogProperty == null)
            {
                Debug.Assert(false);
                return false;
            }
            return (true);
        }

        public void Freeze(Context ctx) {}
    }

AOPSink – It’s our custom interceptor. Here we’ll have the chance to process method invocations.

    public class AOPSink : IMessageSink
    {
        private IMessageSink m_NextSink;
        private ObjRef realobject = null;

        public AOPSink(IMessageSink nextSink)
        {this.m_NextSink = nextSink;}

        public IMessageSink NextSink {get {return m_NextSink;}}

        public IMessage SyncProcessMessage(IMessage msg)
        {
            return m_NextSink.SyncProcessMessage(msg);
        }

        public IMessageCtrl AsyncProcessMessage(IMessage msg, 
             IMessageSink replySink)
        {
            throw new NotSupportedException();
        }
    }

AOPBaseClass – Lastly, we don’t want our users to remember the right derivation and the right attribute marking, so we create a base class for all the classes that will be intercepted, and where we group a common service (more on this later, now don’t worry for the SyncProcessMessage function).

    [AOP]
    public class AOPBaseClass : ContextBoundObject
    {
        public AOPBaseClass() {}

        public virtual IMessage SyncProcessMessage(
            IMessageSink sink, IMessage msg)
        {
            AOPControllerInfo cinfo = AOPConfig.GetAssoc(GetType());
            if (cinfo != null && cinfo.controller != null)
            {
                if (cinfo.matcher.IsMatch(msg))
                    return cinfo.controller.SyncProcessMessage(
                        this, sink, msg);
            }

            return sink.SyncProcessMessage(msg);
        }
    }

Why is it so complex to set up an interception infrastructure, if CBOs exist just to be intercepted? Because the service is primarily intended for .NET Remoting and for cross-AppDomain calls. Basically each invocation is translated in an IMessage object, that represent the original request. It’s hence possible to serialize the message to send it for example to another machine, and so on. We don’t need all of this, but since there is no other simplified support, I’ve tried to reduce at a minimum the needed code.

SyncProcessMessage()

The key function in all this part of code is AOPSink.SyncProcessMessage(). Once we set up a class marked with an AOPAttribute, the above code intercepts all method calls translating them in messages and passing them to this function. In the test code above, you can see that the implementation doesn’t do anything but passing the message to the next sink. The result is that all of this code is absolutely invariant: it’s only slower because of the translations in messages. It’s interesting to look at the performance. This is the test class we’ll use from here on for our tests:

    public class TestClass : AOPBaseClass
    {
        private void Output(string s)
        {
            //Console.WriteLine(s);
            MessageBox.Show(s);
        }
        
        public void Test1() { Output("Test1"); }
        public void Test2() { Output("Test2"); }
        public void Test3() { Output("Test3"); }
        public void Go() { Output("Go"); }
    }

Ok, we have some doubtly useful classes, but at least the test class is straightforward: just derive it from AOPBaseClass and you’re done!

The comparison, as usual, is done calling a lot of times a function (say, Test1), commenting and uncommenting the derivation in TestClass. Calling 100,000 times the ‘straight’ class gives us as usual a result of 31, while removing the comment the time goes up to 1,188, always on average. Things are getting a little worse, that’s a big difference; but the absolute time of a method invocation, on average, is 0.012 milliseconds, or about 12 microseconds. While this can start to be a problem for computationally intensive applications, it should still be absolutely bearable for the average application (say 80%?)

Now we have a complete interception mechanism: if you want, you can stop reading this article! You know how much it will cost, and what you do inside the SyncProcessMessage is completely up to your fantasy. For example, you can invoke twice the underlying method, just writing:

    m_NextSink.SyncProcessMessage(msg);
    return m_NextSink.SyncProcessMessage(msg);

Maybe you will lose the first result, but if you can imagine a scenario in which this can be useful (I could give you a hint: suppose you want to modify a database application to write your data toward two different sources at the same time...), you can go for it: I don’t know if this could be possible with available AOP libraries (can someone let me know about this?)

AOP basics

Once solved the technological problems we can go further on to build a useful infrastructure to provide AOP services. The first, classical, steps must deal with pre-processing and post-processing of method invocations.

Here we can leverage our experience and fantasy, and the design I present is of course only one of the possible.

I start by saying that I don’t like attributes that much. They are pretty, easy to write and use but they are intrinsecally static: once you mark with an attribute, you don’t have any chance to modify it. I’ve seen many architectures based on attributes, and each time I wonder if they can be handled properly. Some quick examples, based on the typical AOP examples, are the following: when AOP is used for logging, often we need to log certain classed and not others, maybe sometimes we want to log only starting from some point in time on, and obviously sometimes we need to log for a certain subset of methods and sometimes another, but in the same run of the same application.

From the AOP point of view, we need some sort of control over all of these aspects, and because of this many AOP implementations offer feature-rich languages with which express some or all of these semantics. Of course these implementations must offer some sort of processing of such constructs, such as compilers, precompilers, attribute processers and so on. This seems to me a little overkill, because it’s very difficult to provide a language that can cover all of the needs of heterogeneous applications, or attributes that can be customized in every direction. Lastly, I’ve read a lot of documents in which is stated that is an advantage to be able to group an aspect in a single code point (typically a class), but noone points out that even aspects could need to be architectured: for example, I could want to log method invocations of a class in a manner, and others in others, or even log to a file until a certain condition is met, and from there on throwing exceptions.

I find difficult, in theory and in reality, to be able to create a complex architecture that can react to all of this, without even considering the time that programmers have to spend to learn and practice with such an architecture: remember, we all have deadlines!

When facing the problem of the design, I always start with a simple concept: I think that the best tool with which to model any behaviour is code. Simple as that! This is true for who provide a service AND for who uses that service. Any other kind of solution has the risk (not the certainty, but the risk) of limiting the possibile uses and variants of that service.

Having said all of this, that in some way explains my approach, my goal is to provide a very simple base service, written in a manner that can be immediately comprehensible and above all totally customizable. At the same time, I don’t want to fill original classes with any sort of code, apart from the derivation from the base class, as seen.

The controller

Central to my vision is the controller: simply stated, this is a class that implements the message processing representing method invocations. Its form is very understandable:

    public class AOPBaseController
    {
        public virtual void Begin(object o, IMessage msg) {}
        
        public virtual void End(object o, IMessage msg) {}
        
        public virtual IMessage SyncProcessMessage(object o, 
             IMessageSink sink, IMessage msg)
        {
            Begin(o, msg);
            IMethodReturnMessage returnedMessage = 
(IMethodReturnMessage)sink.SyncProcessMessage(msg);
            if (returnedMessage.Exception != null)
                End(o, msg);
            return returnedMessage;
        }
    }
    

As we can see from the code, it has a virtual function that processes messages in a standard form: before and after the original method invocation it offers the chance to intercept such events (by calling two other, void, virtual functions). Any of these functions is virtual. Maybe usually you will want to implement in a derived class only the Begin() and End() functions, or maybe you want to reimplement the whole interceptions totally, depending on your needs (maybe because you want to handle some sort of return type in the Begin() and End() functions).

As usual, I pay a lot of attention to not being obstrusive at all: any service offered can be tailored or even substituted at pleasure. We also need a way to associate a controller to a class: the simple solution could be the classical attribute, but I’ve already said that I consider it a simplified solution. What if I want to exploit different interception control in two different runs of the application? We need to separate even this service, and I provide another very simple class just for this.

    public class AOPConfig
    {
        // <Type, AOPControllerInfo>
        private static Hashtable associations = new Hashtable();

        public static bool Enabled = true;

        public static void SetAssoc(Type classtype,
             Type controllertype, IMessageMatcher matcher)
        {
            ConstructorInfo ci = controllertype.GetConstructor(Type.EmptyTypes);
            AOPBaseController controller = (AOPBaseController)ci.Invoke(null);
            associations[classtype] = new AOPControllerInfo(controller, matcher);
        }

        public static AOPControllerInfo GetAssoc(Type classtype)
        {
            return (AOPControllerInfo)associations[classtype];
        }
    }

(I do not delve now with the AOPControllerInfo class, the IMessageMatcher interface and the exception handling)

With these static functions we are free to read the associations from an XML file, or asking them to another service, or even, like we do in the sample code, set them in code at the start of the application.

    AOPConfig.SetAssoc(typeof(TestClass), typeof(TestClassController), null);

With these base classes, it’s very easy to write a simple controller:

    public class TestClassController : AOPBaseController
    {
        private void Output(string s)
        {
            //Console.WriteLine(s);
            MessageBox.Show(s);
        }

        public override void Begin(object o, IMessage msg) { Output("Begin"); }
        public override void End(object o, IMessage msg) { Output("End"); }
    }

This sample doesn’t redefine SyncProcessMessage, but intercepts every call to the underlying class to provide pre and post processing. You will notice that every function receives as parameters an object and a message: you can correctly imagine that the first is the underlying object and the second the message representing the method invocation. In the first draft I tried to translate the message in a more readable structure, but in the end I left it untouched: it’ll be enough to read the documentation to understand what you can find in it. The important thing is that in the controller we have all the possible information about the current method invocation, that let us be able to customize totally the behaviour of the interception itself.

Sink revisited

Passing the underlying object around has requested some investigation, but it could be possible with a little variant of the custom sink, that is:

    public class AOPSink : IMessageSink
    {
        private IMessageSink m_NextSink;
        private ObjRef realobject = null;

        public AOPSink(IMessageSink nextSink)
        {this.m_NextSink = nextSink;}

        public IMessageSink NextSink {get {return m_NextSink;}}

        public IMessage SyncProcessMessage(IMessage msg)
        {
    IMethodMessage methodMessage = (IMethodMessage)msg;

            if (methodMessage.MethodName == ".ctor")
            {
                IMethodReturnMessage ret = 
(IMethodReturnMessage)m_NextSink.SyncProcessMessage(msg);
                realobject = (ObjRef)ret.ReturnValue;
                return ret;
            }
            else
            {
                AOPBaseClass obj = 
     (AOPBaseClass)RemotingServices.Unmarshal(realobject);
                return obj.SyncProcessMessage(m_NextSink, msg);
            }
        }

        public IMessageCtrl AsyncProcessMessage(IMessage msg, 
               IMessageSink replySink)
        {
            throw new NotSupportedException();
        }
    }
    

In the message processing we have to intercept the constructor invocation, and save for future use in the sink itself the reference to the just constructed object. See ‘Limits’ below for (evident) limitations of this solution. From now on, every time the sink intercepts an invocation it passes this reference down the chain. This also implies that for each underlying object there are a different attribute, a different property and a different sink. I consider the situation at this point another ‘good’ solution: from now on the programmer is free to create his/her own solution, building on these base classes and redefining every aspect of the provided services.

Message Filtering

We can provide another base service that’s common to AOP implementations: the chance to select which method invocation to filter. This can be done in many ways, even if our test code consider only a pattern matching on the method name. What is important, as always, is to clearly separate this further aspect and to provide the chance to modify dynamically the filtering. I created a simple interface that doesn’t need to be explained:

    public interface IMessageMatcher
    {
        bool IsMatch(IMessage msg);
    }
    

An object implementing this interface can be registered with the controller, and at runtime every intercepted message, apart from the constructor, will be matched against this function in the AOPBaseClass. This is the example matcher in the code:

    public class RegExMethNameMatcher : IMessageMatcher
    {
        private Regex r = null;

        public RegExMethNameMatcher() : this("") {}
        public RegExMethNameMatcher(string filter)
        {r = new Regex(filter);}

        public bool IsMatch(IMessage msg)
        {
            if (msg is IMethodMessage)
                return r.IsMatch(MethodMessageUtil.GetMethodName(
                         (IMethodMessage)msg));
            return false;
        }
    }

I've put this class in the base library because is of common use: it's very typical to filter methods by their name. A regular expression is used to control if the current method invocation is to be filtered in or out. If you build it with an empty string it will always return true.

Again, feel free to let run your fantasy, imagining for example that you can implement a matcher that can react dynamically to external conditions to filter different kind of functions (for example, only properties or only methods). Of course this is a false filter, in the sense that the sink will in every case intercept all method invocations, but this can be very useful to make code more readable, relieving pre and post processing code from looking for the right methods to process.

Minimalia

There are other little details I didn’t explain until now. For example, AOPConfig offers a simple boolean (Enabled) that you can use to block the interception service. Again, this is a false blocking because I tried to give the application the original performance, but it looks impossible from code. Moreover, the sink is able to react even to asynchronous method invocations, but this is beyond the scope of this article. To conclude, I added a class obtained from the source code already credited [4] that provides an utility to obtain useful information from the message, MethodMessageUtil. For the rest, browse and debug at least once the code line by line and you will understand better than words how all of this behaves. If you have any kind of problems, feel free to ask me: I don’t know if I’ll be able to answer, but I promise I’ll try!

Sample code

I wrote and actually use a version of this code in C# 2.0 Beta, so using generics and other new characteristic of the language. I rewrote it in C# 1.X (I hope not too simplified!) version so that more users can run and quickly understand it. There is no error and exception handling, only code core. The only point that needed to be considered, and it is in the sample code, is that the underlying method invocation can raise an exception. The base controller version of the interception framework can see it, and in the sample code simply doesn’t call the End() processing. In real world applications, you will want to design a reasonable handling code for this case.

Limits

I'm not trying to fool you: this is not the perfect solution. It may have some good points, but also bad points. A real AOP library is still by far a solution much much better than mine, no point on this. Here the bad part goes:

- Performance. In the end, my tests show that in typical scenarios you can lose at worst 0,030 microseconds for each intercepted call (if you use a typical nowadays PC). I don't take into account what I do in the interception code: this cost is only for the infrastructure, and you spend it for each method invocation in an intercepted class, even if you don't do anything or filter out the call. It means also that you need some 33,000 calls to lose one whole second. If it's too much for you, only you know. There is a little trick to commute an application between intercepted mode and non intercepted: adding an intermediate class in the chain from your classes to AOPBaseClass.

    public class AOPCommutingClass : AOPBaseClass
    {
    }
 

If you derive all of your classes from this one, it'll be enough to comment out the derivation here to have a 'normal' program again (only a bit cluttered by the AOP classes you don't use anymore).

  • Memory. Sincerely, I don't bother for this problem anymore, but you have to know that ContextBoundObjects consume memory; the property, the sink, and so on, each one consumes memory.
  • Capabilities. While it can be reasonable enough that you are not able to intercept static functions, maybe you didn't notice you can't intercept constructors, or better, you have to change my design. But usually this shouldn't be a problem, because your application objects (natural candidates for interception) should be instantiated by some class factory with some method (that can be intercepted): try to think about this...

References

[1] - Aspect Oriented Programming / Aspect Oriented Software Design – Marc Clifton (http://www.codeproject.com/gen/design/aop.asp)

[2] - Ted Neward's Personal Weblog (http://www.neward.net/ted/weblog/index.jsp?date=20030107)

[3] – Many argue, and with a lot of good reasons, against the use of ContextBoundObject as a base for AOP. Google for contextboundobject aop to see why.

[4] – Build an AOP.NET Extensible Business Component using ContextBoundModel (http://www.codeproject.com/dotnet/ContextBoundModel.asp)

[5] – Decouple Components by Injecting Custom Services into Your Object's Interception Chain (http://msdn.microsoft.com/msdnmag/issues/03/03/ContextsinNET/default.aspx)

[6] – Aspect-Oriented Programming Enables Better Code Encapsulation and Reuse (http://msdn.microsoft.com/msdnmag/issues/02/03/AOP)

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

Share

About the Author

Andrea Bioli
Web Developer
Italy Italy
Frog Software srl is an Italy based training and consulting company, specialized in .NET technologies. It's available in-house and in-place all over southern Europe.
Andrea Bioli is the founder, and has more than ten years of experience in C/C++ design and programming on large projects.
Since .NET launch, Frog Software has focused exclusively on this technology.
http://www.frogsoftware.it (in italian)

Comments and Discussions

 
QuestionCalls from one method to another is not getting intercepted Pinmemberjoygk23-Aug-11 22:06 
GeneralSystem.Runtime.Remoting.RemotingException PinmemberHaroldBalta11-May-10 7:33 
GeneralVery nice Pinmembermikeperetz5-Sep-09 8:18 
GeneralGood Job ! Pinmembergpgemini4-Jul-09 5:21 
Questionhow to intercept recursive calls? PinmemberSaeed Alg28-Apr-08 5:35 
QuestionCalling a method from within a AOPBaseController PinmemberKevin Kinnett31-Jul-07 8:17 
QuestionContextBoundObject with Generics Throws Exception Pinmemberivantorresjmz30-Aug-06 18:00 
AnswerRe: ContextBoundObject with Generics Throws Exception PinmemberMohammad Reza Jooyandeh3-Feb-08 0:21 
Generalaborting method execution Pinmembercre_Active26-Feb-06 3:34 
GeneralNot in the web PinmemberIsta2-Dec-05 7:04 
GeneralAccess to calling object PinmemberDewyDewhirst24-Aug-05 14:00 
GeneralRe: Access to calling object Pinmemberandreabioli28-Aug-05 5:24 
QuestionRe: Access to calling object PinmemberSaeed Alg28-Apr-08 5:18 
GeneralStrange, I have quite differnet performance test results PinsussAlexander Pinsker22-Aug-05 1:15 
GeneralRe: Strange, I have quite differnet performance test results Pinmemberandreabioli28-Aug-05 5:17 
GeneralCastle.DynamicProxy PinsussS3an7-Apr-05 5:58 
GeneralVery interesting PinmemberNemanja Trifunovic30-Sep-04 6:15 
GeneralRe: Very interesting PinmemberAndrea Bioli30-Sep-04 6:34 
GeneralRe: Very interesting PinsussAnonymous7-Mar-05 14:31 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web04 | 2.8.141223.1 | Last Updated 30 Sep 2004
Article Copyright 2004 by Andrea Bioli
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid