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
Post Footer automatically generated by Add Post Footer Plugin for wordpress.
