All developers have the same problems

During the development of any application or information system, we have several aspects to cover and implement. Although the business logic is the main motivator and driver behind the implementation, it is merely a small part of a large project. Ignoring the other aspects will probably result in a rogue project that is literally lacking security, unmaintainable, buggy and the bugs are hard to track down and fix.

For those reasons, developers implement functionalities that are aiming to solve those problems. Some of the most important aspects are:

  • Authorization
  • Error handling
  • Benchmarking
  • Error logging
  • Multi language translations
  • Configurations

All of those functionalities represent independent features acting as independent vectors and should never be crossing functionalities, but usually involves a lot of work to implement their meaning across all layers of the application. Another implication of this implementation is very often to see the same code again and again in the form of boilerplate code, just because it "has to be there".

public class MyWebService
{
public bool foo()
{
try
{
return SomeMicroService.RunFoo();
}
catch (Exception ex)
{
return false;
}
}
}

Try to imagine having a similar class containing 10 methods like this, while it is important to notice, that this code is a very big understatement in terms of Error handling. Let's take into account some added error logging:

public class MyWebService
{
public bool foo()
{
try
{
var result = SomeMicroService.RunFoo();
LogService.Log(result);
return result;
}
catch (Exception ex)
{
LogService.LogError(ex);
return false;
}
}
}

Let's bring in some Authentication and benchmarking.

public class MyWebService
{
public bool foo()
{
DateTime startDate = DateTime.Now;
try
{
if(Authentication.CheckUser(Context.CurrentUser))
throw new NotAllowedException
LogService.Log(result);
return result;
}
catch (Exception ex)
{
LogService.LogError(ex);
return false;
}
finally
{
DateTime stopDate = DateTime.Now;
LogService.LogBenchmark(stopDate - startDate, "Foo");
}
}
}

As you can see, we have implemented only a couple of those boilerplate functionalities in a rudimentary way, but you can already see that this code is blowing up like a bubble rapidly. We added about 20 lines of code, only for the need of a single line (yellow highlighted), that represents the almighty business logic, to be executed. This example is only one method in a single class that represents a web service and it's quite often that they tend to have 10-20 methods.

Now consider the underlying classes that require the same functionality and the same code to be copy/pasted across methods and classes. Consider implementing new functionalities to be added or existing functionalities to be removed and how much time would it require a single developer to implement them. Let's go a step further. How many lines of code could be deleted or ignored by a new developer joining the project? I hope you see the problems that we are expecting with it.

How can we solve this problem

AOP (Aspect Oriented Programming) is a software paradigm aiming to increase modularity by allowing the separation of cross-cutting concerns. It suggests us not to mix code of different functionalities, but to enrich the existing code with envelops of new code. The existing and most important code for us is the business logic code and therefore this is the code we need to enrich in order to be able to observe it. Some programming languages, like Python support it natively using decorators, but unsupported languages, like C# require assistance.

AOP brings the term Aspect to the table. It represents a separated concern and functionality that is supposed to decorate and enrich the main scope of the project. One of those aspects could be the Error logging. We would create an envelope around the BL (business logic) that would handle the logging. Afterwards we could create another envelope/aspect that would benchmark a method call, or any other of the mentioned functionalities.

In C#, some libraries have taken this task on them self to deliver a functional capability. In this article we will reflect on the library PostSharp, that has proven itself as a serious contender, but also offers a very detailed documentation, instructions and training.

So, how do we implement such functionality but not overcomplicate the code with too many layers of abstraction? Let's create a new class called ConsoleLogAspect and inherit from OnMethodBoundaryAspect (PostSharp.Aspects namespace) as a Serializable class.

[Serializable]
public class ConsoleLogAspect: OnMethodBounaryAspect { public override void OnEntry(MethodExecutionArgs args) { } public override void OnExit(MethodExecutionArgs args) { } public override void OnException(MethodExecutionArgs args) { } }

 

It offers some virtual methods that can be overridden, but only the basic ones already solve some of our issues. OnEntry is executed before our BL, OnExit after the BL and OnException when an Exception occurs. Those methods contain an input parameter MethodExecutionArgs containing reflection data that can be used to identify the object this aspect is decorating and in which way the decoration is happening

  • args.Arguments – method input arguments
  • args.ReturnValue – the return value of the decorated method
  • args.YieldValue – the yield return value of the decorated method
  • args.Exception – The thrown Exception
  • args.Method – The reflection method instance
  • args.Instance – The object decorated with the aspect

Let's create a BL class and method that we would like to decorate. In that Example we will include AOP into a command Pattern. All we need to do, in order to decorate a method or a class with an aspect, is to decorate it with an attribute of the aspect's name.

public class ConsoleMessageCommand : ICommand
{
    public event Event Handler CanExecuteChanged;

	private int _executionCount = 0;

	public bool CanExecute(object parameter)
	{
		if (parameter is int) {return true;}
		else if (parameter is string) {return false;}
		else {throw new Exception("wrong parameters");}
	}

	[ConsoleLogAspect]
	public coid Execute (object parameter)
	{
		if (CanExecute(parameter))
		{
			_executionCount++;
			Console.WriteLine(this.GetType().Name +"Execute:"+ _executionCount);
		}
	}

}

Let's try to execute the method call and see the producing result.

class Program
{
static void Main (string[] args)
{
ICommand cmd = new ConsoleMessgaeCommand();
cmd.Execute(1);
Console.ReadKey();
}
}

 

Aspect Oriented Programming

We are not limited to decorating only a method but we can also decorate the whole class. By doing so, we get the opportunity to have an Advice (aspect method) being executed on every method call, including the constructor. Having more control on the constructor execution, gives plenty of opportunities for Dependency injection resolution.

=Example 1
In the last example, we have seen how we can implement a very lean and elegant way to implement error logging and error handling in a very quick and easy way. But there are also other simple appliances, like the following that implements a very simple benchmarking by logging the method executed, and how long the execution took.

[Serializable]
[AttributeUsage(AttributeTargets.Method)]
public sealed class LogExecTimeAttr: OnMethodInvocationAspect
{
private static ILog Log = LogMngr.GetLogger (typeof(LogExecTimeAttr));
public LogExecutionTimeAttribute() : this(int.MaxValue, true) { }
public LogExecTimeAttr (int threshold) : this(threshold, false) { }
public LogExecTimeAttr (int threshold, bool logDebug)
{
Threshold = threshold;
LogDebug = logDebug;
}
public int Threshold { get; set; }
public bool LogDebug { get; set; }
//Record time spent executing the method
public override void OnInVocation(MethodInvocationEventArgs eventArgs)
{
var start = DateTime.Now;
var timeSpent = (DateTime.Now - start).TotalMilliseconds;
if (LogDebug)
{
Log.DebugFormat(
"Method [{0}{1}] took [{2}] milliseconds to execute",
eventArgs.Method.DeclaringType.Name,
eventArgs.Method.Name,
timespent);
}
if (timeSpent > Threshold)
{
Log.WarnFormat(
"Method [{0}{1}] was expected to finish within [{2}] milliseconds, took [{3}] instead!",
eventArgs.Method.DeclaringType.Name,
eventArgs.Method.Name,
Threshold,
timeSpent);
}
}
}

In this example we have created a simple aspect that can invokes a decorated method, at a point of your choosing (the yellow highlighted line). The aspect will be executed only when methods are called and ignored on properties ([AttributeUsage(AttributeTargets.Method)]). There are many more options and configurations that allow you full control on what method can be decorated in which ways. 

Example 2

Another great example of AOP is how to bring more controlled chaos into Unit Testing. The following example shows how to decorate a dependency with a failure probability. This way we can implement a more reliable error handling by bringing more chaos and unexpected behaviors simulating failing web service.

[Serializable]

public class CallMeMaybeAspect : MethodInterceptionAspect
{
private readonly Type exceptionType;
private readonly double probability;
public CallMeMaybeAspect(double probability = 0.9, Type exceptionType = null)
{
this.probability = probability;
this.exceptionType = exceptionType ?? typeof(InvalidOperationException);
if (!typeof(Exception).IsAssignableFrom(this.exceptionType))
{
throw new ArgumentException("Must be a System.Exception",
"exceptionType");
}
}
public override void OnInvoke(MethodIntercetionArgs args)
{
double chance = (new Random()).NextDouble();
if (chance < this.probability)
args.Proceed();
else
{
//Replace with exception.
var ex = (Exception)Activator.CreateInstance(this.exceptionType);
throw ex;
}
}
}

Every time we call the method, decorated with this Aspect, there is a desired probability that it will throw an exception. This can bring us more reliability while testing a class on its reliability.

Other examples
There are exceptional exceptional examples of how AOP can be used for various purposes.

  • Security – Security is a really great example of a simple way to enforce secure access to a web service, even if the access is managed through a complicated role based access enforcement
  • Undo/Redo – Usually, it takes a lot of effort to implement an Undo/Redo functionality including a complicated combination of a Command and Converter pattern. The AOP approach simplifies the implementation significantly, and it is also covered as tutorial example in the documentation
  • WPF GUI notification – Whoever was doing any WPF development, has implemented the INotifyPropertyChanged interface, too many times and raised its event even more times. Let me tell it to you this way, „It tends to become really tedious“. AOP offers us possibilities to handle these implementations in a very painless way. One really great example is Fody's (Another AOP weaver – not PostSharp) implementation.
  • Cache management – If we have multiple levels of caching in multiple classes, this is a very simple way to accessing lower levels of slower cache mechanisms
  • Multi language translations – Some developers have been using AOP to translate their applications into different languages by creating a single Aspect
  • Post implementation configurations

    Since, we need to decorate methods and classes the way we require it, does this mean that we need go through our hundreds of classes and define all of them manually one by one? Absolutely not. We have advanced ways on applying aspects to classes on an assembly level.

    1. We can define a decoration an assembly level that is equal to direct class decoration
      [assembly: PostSharpDemo1.MethodTraceAspect( AttributeTargetTypes = PostSharpDemo1.InMemoryDataStore")]
    2. We can also define decorations also using wildcards on the whole namespace level
      [assembly: PostSharpDemo1.MethodTraceAspect( AttributeTargetTypes = "PostSharpDemo1.Data.*")]
    3. We can also define decorations using Regular expressions
      [assembly: PostSharpDemo1 MethodTraceAspect(AttributeTargetTypes = " regex:.*Memory.*")]
    4. But we can have even more control, like excluding the methods from decorating
      [assembly:PostSharpDemo1.MethodTraceAspect(AspectPriority=0, AttributeExclude=true,AttributeTargetMembers="regex:get_.*|set_.*")]

    A very common question that arises is related to, the execution order of the Aspects. Since, a method can be decorated with multiple aspects, how can we control the order of the aspects and which will envelop over which, during build and execution time. The answer is actually very simple. Every Aspect can be configured using a priority. The lower the number, that higher the priority.

    [Trace(AttributePriority = 2)] [HandleError(AttributePriority = 1)] public void MyMethod () { }

    If you are implementing AOP into an existing project, using only some of those configurations, will give you the possibility to implement your aspects in a short amount of time.

    Summary

    We have given you a simple overview of the AOP capabilities in C# using PostSharp. If you choose to utilize it, you can expect some changes in your code. So what can you expect?

    1. The code, taking care of your business logic, becomes much easier to read, since you have moved your cross-concern code to separate places
    2. Less repetition of boilerplate code implies the number of defects and bugs, but also shorter development times
    3. Less experienced developers would not be handling the advanced AOP method weaving, but can focus on the simple Business logic implementation. Since they do not need to bother with advanced topics they are easier to onboard for projects.

    But there are also some drawbacks to expect. PostSharp works by intercepting your application and modifies it's "Microsoft intermediate language - MSIL." This makes it impossible to debug aspects in the usual way, but there are inspector tools taking care of that. The only problem I have experienced, so far with it, is that build errors tend to show up, that are not actually errors and the actual build error can be ignored and not listed. This can pose a big problem, but this usually happens, when developers try to implement hacks that they should not be doing. 

    In this sense, AOP is just like any other paradigm and technology. If you decide to mess with it, it will mess with you. AOP code is an advanced topic and I would highly recommend you not to leave it to your junior developer to implement it.