Aspect-oriented approaches to software development

A while back, I wrote a little post alluding to ways to reuse certain processes that are duplicated in code throughout any given system (event logging, in my example). In this post, I’d like to show you how I implemented a possible solution using aspect oriented programming.


What is Aspect Oriented Programming?


Basically, the idea that there are types of processes that occur in multiple places in a system, commonly referred to as “cross-cutting concerns”. Aspects are implementations of these concerns, written so that they can be reused in a system. In the logging example, the need to log exceptions is a concern, and the aspect is a piece of code that fulfills this need. The idea behind aspect oriented programming is to identify these cross-cutting concerns, create aspects to manage them, and introduce their behaviors into the points of a system that their behavior needs to govern. In my example, I will accomplish this by creating some custom attributes and aspects as well as decorating classes and methods. As these objects are instantiated and their methods are invoked, I will be intercepting messages and injecting the behavior defined by my aspect.


A Consumer’s View


Here is my simplistic implementation: 


public class HelloWorld
{
    
public void SendGreeting()
     {
           
Messenger messenger = new Messenger();
            messenger.SendMessage(
“Hello World!”);
     }
}


Running this code produces the following “logged” output:


 


Attempting connection…
Logging Exception: A problem occurred!


 


All right, so how does this work? Here’s my messenger class.


[Loggable]
public class Messenger : ContextBoundObject
{
     [
LogException]
    
public void SendMessage(string message)
     {
         
Console.WriteLine(“Attempting Connection…”);
         
throw new Exception(“A problem occurred!”);
     }
}


First thing you might notice is that it inherits from a class called ContextBoundObject. By inheriting from this class, we will recieve the context needed from the runtime and intercept messages in order to extend the invocation of SendMessage to include the behavior defined by our aspect. Also, there are two different types of attributes being used here… [Loggable] and [LogException].


[Loggable]


The [Loggable] attribute is a context attribute used to associate our Aspect into the message chain. Below is the source code.


[AttributeUsage(AttributeTargets.Class)]
public class LoggableAttribute : ContextAttribute, IContributeObjectSink
{
     
public LoggableAttribute() : base(“Loggable”) { }

     
public override void GetPropertiesForNewContext(IConstructionCallMessage msg)
      {
            msg.ContextProperties.Add(
this);
      }

      public IMessageSink GetObjectSink(MarshalByRefObject obj, IMessageSink nextSink)
      {
           
return new LogExceptionAspect(nextSink);
      }
}


The implementation here is fairly straight-forward. The attribute can only be used with classes and when it is instantiated, it will add itself to the message’s context and add our aspect to the message chain for execution. Also, when instantiating LogExceptionAspect, we give it a reference to the next message sink in the chain, allowing us to insert our aspect as a link in the chain.


[LogException]


The [LogException] attribute is a simple attribute that houses our logging code, and can only be used on methods.

[AttributeUsage(AttributeTargets.Method)]
public class LogExceptionAttribute : Attribute
{
    
public void Process(string message)
     {
         
new EventLogger().LogMessage(message);
     }
}

LogExceptionAspect


The LogExceptionAspect class, which is part of the [Loggable] context attribute’s implementation, is a message sink. When the chain of message sinks is being processed, the runtime will get a reference to this class and invoke IMessage.SyncProcessMessage. This function will house code related to our logging process.

public class LogExceptionAspect : IMessageSink
{
      
private readonly IMessageSink nextMessageSink;

      
public IMessageSink NextSink
       {
           
get { return this.nextMessageSink; }
       }

       public LogExceptionAspect(IMessageSink nextMessageSink)
       {
           
this.nextMessageSink = nextMessageSink;
       }

       public IMessage SyncProcessMessage(IMessage msg)
       {
            
MethodBase method = RemotingServices.GetMethodBaseFromMethodMessage((IMethodMessage)msg);
           
           
FormatExceptionAttribute formatException = (FormatExceptionAttribute)Attribute.GetCustomAttribute(method, typeof(FormatExceptionAttribute));
            
LogExceptionAttribute logException = (LogExceptionAttribute)Attribute.GetCustomAttribute(method, typeof(LogExceptionAttribute));
              ReturnMessage response = (ReturnMessage)this.nextMessageSink.SyncProcessMessage(msg);

            if (response.Exception == null) return response;

            string message = response.Exception.Message;

            if (formatException != null) message = formatException.FormatException(response.Exception);
            if (logException != null) logException.Process(message);

            return response;
      }

      public IMessageCtrl AsyncProcessMessage(IMessage msg, IMessageSink replySink)
      {
           
return this.nextMessageSink.AsyncProcessMessage(msg, replySink);
      }
}


As you can see, in this aspect, we get a reference to the method and check for possible attributes ([LogException] and [FormatException]) and run the rest of the chain. At this point in execution, exceptions do not get thrown, so there is no need for any try/catch constructs. Simply check the resulting message for exceptions, and if any are detected, format and log the exception if the function has been decorated with attributes directing the aspect to do so.


As you can see in the above example, I have introduced another attribute called [FormatException]. This is simply to illustrate that multiple attributes can be associated with the aspect and you can decorate your functions as desired with one, both or neither directives for the aspect to adhere to. Here is the definition for [FormatException]:


public class FormatExceptionAttribute : Attribute
{
    
public string FormatException(Exception exception)
     {
        
return new EventFormatter().FormatException(exception);
     }
}


and the definitions for the logger and event formatter:


public class EventLogger
{
    
public void LogMessage(string message)
     {
        
Console.WriteLine(“Logging exception: “ + message);
     }
}


public class EventFormatter
{
     
public string FormatException(Exception exception)
     {
         
return “Formatted Exception: “ + exception.Message;
     }
}


If I add [FormatException] in my Messenger class, the output changes to:


Attempting connection…
Logging Exception: Formatted Exception: A problem occurred!



In Closing

I see some nice benefits and some unfortunate drawbacks to the way .NET handles this. The need to inherit from ContextBoundObject is something I really don’t like and causes me to be hesitant in trying to implement something like this. Additionally, it would require a certain level of competancy for anyone that wants to maintain this code. However, this does prevent a lot of try/catch/log exception/throw; code repetition and could be altered to allow one to easily inject whatever sort of logging/formatting mechanisms they want to use, or even actions they want to take based on type of exception. Furthermore, it allows developers to write try/catch statements only in cases where they want to handle exceptions and resolve them, and it allows developers that use it to focus on writing code to accomplish the task at hand instead of writing code (yet again) to log exceptions that arise in the system.

Related Articles:

Post Footer automatically generated by Add Post Footer Plugin for wordpress.

This entry was posted in Uncategorized. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • http://www.lostechies.com/blogs/evan_hoff/default.aspx Evan

    nice post..i digg it ;-)

    i may play off this one in an upcoming post..

  • http://anydiem.com Sean Scally

    I really do like the idea of being able to reduce the complexity of code by pulling parts that don’t really matter to the task at hand (like logging) to keep the logic simple. On the other hand, I don’t see yet what this buys me over an approach like using the EntLib logging framework at application boundaries.

  • mbratton

    The Logging Enterprise Application Block was, the last time I used it, (admittedly I haven’t used it in a while) a cool way to manage where stuff got logged. In fact, I think you could use this approach and the LEAB, rather than write your own logger (if configurability is a big issue).

    I’m not 100% certain, but I think that with the LEAB you still have to write the try/catch/rethrow sections of code and delegate to the LEAB each time you want to log. Or has this changed since I last saw it implemented?

    I have heard that there is something similar to this in WCF with exception shielding, which I think is cool.