Using Aspects in C# With POSTSharp
A little bit about aspect oriented programming (AOP)
For the last few couple of years, as the hardware kept getting cheaper and more powerful, the programming languages core concern has been moved from performance to clarity and maintainability.
While in the past you had to keep asking yourself which algorithm was the best to, say, iterate over a list, now it is far from being a major concern. What it gives us, developers, is a chance to focus our precious time and effort on more important matters in our daily jobs, like readability and maintainability.
With these new priorities in mind, the industry have come up with the object oriented paradigm - which focus on mapping real world entities straight to code equivalents.
However, a big troublemaker we still face every now and then while developing software are the crosscutting concerns - generating replicated code all around our projects. The main examples I can come up with about this are:
- Logging;
- Authentication;
- Open/Close connections;
- Exception handling.
These concerns not seldom can be seen being dealt with using very similar (if not the exact same) blocks of code alongside many of our classes, even though it is a relatively unique matter.
To address this problem came the aspect oriented paradigm. Its purpose is to complement the OOP, eliminating the need for replication and adding clarity to your code. What follows in this article is a very quick example of the possible benefits that using aspects can bring you while developing software.
Learning by Example
The immediate concern I have about having code all over the place to handle a concern that should be centralized is that, when this thing needs to be changed for some reason, you will have to manually go to every point where the code is replicated and fix it. Every good programmer knows that manual tasks like this are bound to catastrophic failure.
Next, let’s implement an Account class, that will provide deposit and withdraw functionality. We shall start, of course, by the tests:
[TestMethod]
public void WithdrawShouldDecreaseBalance()
{
Account account1 = AccountBuilder.New
.BelongingTo("MACSkeptic")
.WithInitialBalance(200.95M)
.IdentifiedBy(773)
.Instance;
Assert.AreEqual(100.95M, account1.Withdraw(100).CurrentBalance);
Assert.AreEqual(0.0M, account1.Withdraw(100.95M).CurrentBalance);
Assert.AreEqual(-10.0M, account1.Withdraw(10.00M).CurrentBalance);
}
[TestMethod]
public void DepositShouldIncreaseBalance()
{
Account account1 = AccountBuilder.New
.BelongingTo("MACSkeptic")
.WithInitialBalance(-50.42M)
.IdentifiedBy(666)
.Instance;
Assert.AreEqual(-0.42M, account1.Deposit(50).CurrentBalance);
Assert.AreEqual(79.58M, account1.Deposit(80).CurrentBalance);
}
To satisfy the behavior denoted just above we shall implement the Account class, like this:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Codevil.MACSkeptic.PostSharpExample.Entities
{
public class Account
{
public long Number { get; set; }
public string Owner { get; set; }
public decimal CurrentBalance { get { return this.currentBalance; } }
private decimal currentBalance;
public Account(long number, string owner)
: this(number, owner, 0)
{
}
public Account(long number, string owner, decimal initialBalance)
{
this.Number = number;
this.Owner = owner;
this.currentBalance = initialBalance;
}
public Account Deposit(decimal howMuch)
{
Console.WriteLine(string.Format(
"New deposit transaction on account {0}. Balance before operation is ${1}.",
this.Number,
this.CurrentBalance));
Console.WriteLine(string.Format(
"Depositing ${0} on account {1}.", howMuch, this.Number));
this.currentBalance += howMuch;
Console.WriteLine(string.Format(
"Finished deposit transaction on account {0}. Balance after the operation is ${1}.",
this.Number,
this.CurrentBalance));
return this;
}
public Account Withdraw(decimal howMuch)
{
Console.WriteLine(string.Format(
"New withdraw transaction on account {0}. Balance before operation is ${1}.",
this.Number,
this.CurrentBalance));
Console.WriteLine(string.Format(
"Withdrawing ${0} on account {1}.", howMuch, this.Number));
this.currentBalance -= howMuch;
Console.WriteLine(string.Format(
"Finished withdraw transaction on account {0}. Balance after the operation is ${1}.",
this.Number,
this.CurrentBalance));
return this;
}
}
}
As you can see, our account allows for deposits and withdrawals with no concern about the overall balance at the moment. Which means, if you withdraw more than you have, you’re going to get in debt.
To keep things simple and focus on what really matters here, I’m using the console as the log of the application.
By the code above, we can already spot a clear repetition pattern on the logging concern related code. Let’s try to fix it with traditional OO refactoring, extract method:
public Account Deposit(decimal howMuch)
{
this.LogTransactionStarting("deposit");
this.LogTransactionDetails("Depositing", howMuch);
this.currentBalance += howMuch;
this.LogTransactionFinished("deposit");
return this;
}
public Account Withdraw(decimal howMuch)
{
this.LogTransactionStarting("withdraw");
this.LogTransactionDetails("Withdrawing", howMuch);
this.currentBalance -= howMuch;
this.LogTransactionFinished("withdraw");
return this;
}
private void LogTransactionStarting(string action)
{
Console.WriteLine(string.Format(
"New {2} transaction on account {0}. Balance before operation is ${1}.",
this.Number,
this.CurrentBalance,
action));
}
private void LogTransactionDetails(string action, decimal howMuch)
{
Console.WriteLine(string.Format(
"{2} ${0} on account {1}.", howMuch, this.Number, action));
}
private void LogTransactionFinished(string action)
{
Console.WriteLine(string.Format(
"Finished {2} transaction on account {0}. Balance after the operation is ${1}.",
this.Number,
this.CurrentBalance,
action));
}
Ok, that’s a little better now, with a little less repetition, right? But, as you can see, the account class still has to be concerned with keeping a log of its transactions. Using just OO we could move the logging methods to another class, like a service. But still we would have to call them explicitly from inside the account class. The problem with this is that, when you read this code, you will most likely get distracted from the main point (the business logic) because of the logging concerned method calls.
There is a well known saying about how you design your classes on an OOP project: if you have to use an "and" to describe what your class does, that smells like bad design - more specifically, your class is most likely overloaded with responsibilities that shouldn’t be there. In our example so far, the account class is concerned with the account movements and the logging.
Here we can classify the main points at which we wish to add logging:
- Before any transaction;
- Right when the transaction will be executed, with details concerning values involved;
- Right after the transaction.
A very interesting and powerful tool for aspect development in .NET is the PostSharp framework - its slogan: "make sense, not code" says a lot about the main idea of the AOP. For the next tasks on this article, we will need to download PostSharp (I’m using the version 1.5). It can be downloaded clicking here (you will need to register, which is free, by clicking here).
After installing PostSharp, it is time to add references to PostSharp.Laos and PostSharp.Public to our project, like this:

PostSharp’s detailed documentation can be accessed by clicking here.
Our account class will make use of the following aspect concepts/classes:
First things first, let’s get to the on method boundary aspect implementation:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using PostSharp.Laos;
using Codevil.MACSkeptic.PostSharpExample.Entities;
namespace Codevil.MACSkeptic.PostSharpExample.Aspects
{
[Serializable]
public class LogAccountStatusBeforeAndAfterTransaction : OnMethodBoundaryAspect
{
public override void OnEntry(MethodExecutionEventArgs eventArgs)
{
Account account = (Account)eventArgs.Instance;
Console.WriteLine(string.Format(
"New {2} transaction on account {0}. Balance before operation is ${1}.",
account.Number,
account.CurrentBalance,
eventArgs.Method.Name));
}
public override void OnExit(MethodExecutionEventArgs eventArgs)
{
Account account = (Account)eventArgs.Instance;
Console.WriteLine(string.Format(
"Finished {2} transaction on account {0}. Balance after the operation is ${1}.",
account.Number,
account.CurrentBalance,
eventArgs.Method.Name));
}
}
}
The aspect "on method boundary" allows us to determine pieces of code that will be executed right before and right after the original method was called, like you can see in the snippet just above. It is very important to notice that all aspect implementations with PostSharp must be serializable.
Since we moved the "before transaction" and "after transaction" logging concern to this aspect, let’s see how it can help us with our account class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Codevil.MACSkeptic.PostSharpExample.Aspects;
namespace Codevil.MACSkeptic.PostSharpExample.Entities
{
public class Account
{
public long Number { get; set; }
public string Owner { get; set; }
public decimal CurrentBalance { get { return this.currentBalance; } }
private decimal currentBalance;
public Account(long number, string owner)
: this(number, owner, 0)
{
}
public Account(long number, string owner, decimal initialBalance)
{
this.Number = number;
this.Owner = owner;
this.currentBalance = initialBalance;
}
[LogAccountStatusBeforeAndAfterTransaction]
public Account Deposit(decimal howMuch)
{
this.LogTransactionDetails("Depositing", howMuch);
this.currentBalance += howMuch;
return this;
}
[LogAccountStatusBeforeAndAfterTransaction]
public Account Withdraw(decimal howMuch)
{
this.LogTransactionDetails("Withdrawing", howMuch);
this.currentBalance -= howMuch;
return this;
}
private void LogTransactionDetails(string action, decimal howMuch)
{
Console.WriteLine(string.Format(
"{2} ${0} on account {1}.", howMuch, this.Number, action));
}
}
}
We can see the attributes added on lines #27 and #34 - they just mark where the logging aspect is supposed to act.
Now, let’s see how the "on method invocation" aspect works on practice:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using PostSharp.Laos;
using Codevil.MACSkeptic.PostSharpExample.Entities;
namespace Codevil.MACSkeptic.PostSharpExample.Aspects
{
[Serializable]
public class LogAccountTransactionDetails : OnMethodInvocationAspect
{
public override void OnInvocation(MethodInvocationEventArgs eventArgs)
{
Account account = (Account)eventArgs.Instance;
Console.WriteLine(string.Format(
"{2} ${0} on account {1}.",
eventArgs.GetArgumentArray().First(),
account.Number,
eventArgs.Method.Name));
eventArgs.Proceed();
}
}
}
Again, we’ve just moved the original implementation of the logging method (which was once on the account class) to here. Here it is interesting to notice that the eventArgs parameter gives us access to some really neat stuff, like:
- The current instance of the class in which the aspect is being applied;
- The method being called;
- The parameters used on the method call.
Notice the call to the method "proceed" above (line #21)? This method tells the weaver that the original method should be called right at this point. This concept has some immediate consequences, if needed, we could:
- Switch the original parameters with new ones - to do that, you just need to pass the new ones to the proceed method;
- Not even call the original method - to do that, you just need to omit the call to the proceed method altogether.
With this, we’ve just moved every last bit of logging concern straight out of our account class, into our logging aspects. So, let’s see how the account class has changed:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Codevil.MACSkeptic.PostSharpExample.Aspects;
namespace Codevil.MACSkeptic.PostSharpExample.Entities
{
public class Account
{
public long Number { get; set; }
public string Owner { get; set; }
public decimal CurrentBalance { get { return this.currentBalance; } }
private decimal currentBalance;
public Account(long number, string owner)
: this(number, owner, 0)
{
}
public Account(long number, string owner, decimal initialBalance)
{
this.Number = number;
this.Owner = owner;
this.currentBalance = initialBalance;
}
[LogAccountStatusBeforeAndAfterTransaction]
[LogAccountTransactionDetails]
public Account Deposit(decimal howMuch)
{
this.currentBalance += howMuch;
return this;
}
[LogAccountStatusBeforeAndAfterTransaction]
[LogAccountTransactionDetails]
public Account Withdraw(decimal howMuch)
{
this.currentBalance -= howMuch;
return this;
}
}
}
Before anything else, by using a regular expression we can clean up this code even more:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Codevil.MACSkeptic.PostSharpExample.Aspects;
namespace Codevil.MACSkeptic.PostSharpExample.Entities
{
[LogAccountStatusBeforeAndAfterTransaction(
AttributeTargetMembers = "regex:(Deposit)|(Withdraw)")]
[LogAccountTransactionDetails(
AttributeTargetMembers = "regex:(Deposit)|(Withdraw)")]
public class Account
{
public long Number { get; set; }
public string Owner { get; set; }
public decimal CurrentBalance { get { return this.currentBalance; } }
private decimal currentBalance;
public Account(long number, string owner)
: this(number, owner, 0)
{
}
public Account(long number, string owner, decimal initialBalance)
{
this.Number = number;
this.Owner = owner;
this.currentBalance = initialBalance;
}
public Account Deposit(decimal howMuch)
{
this.currentBalance += howMuch;
return this;
}
public Account Withdraw(decimal howMuch)
{
this.currentBalance -= howMuch;
return this;
}
}
}
No more logging concern inside our account class ;).
It gets even better! Now that we have a little better understanding of the PostSharp framework, we can get rid of the "on method boundary" aspect. To do so, we just need to make some adjusts to the "on method invocation aspect", like this:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using PostSharp.Laos;
using Codevil.MACSkeptic.PostSharpExample.Entities;
namespace Codevil.MACSkeptic.PostSharpExample.Aspects
{
[Serializable]
public class LogAccountTransactionDetails : OnMethodInvocationAspect
{
public override void OnInvocation(MethodInvocationEventArgs eventArgs)
{
Account account = (Account)eventArgs.Instance;
LogTransactionStarting(eventArgs, account);
LogTransactionDetails(eventArgs, account);
eventArgs.Proceed();
LogTransactionFinished(eventArgs, account);
}
private static void LogTransactionDetails(
MethodInvocationEventArgs eventArgs, Account account)
{
Console.WriteLine(string.Format(
"{2} ${0} on account {1}.",
eventArgs.GetArgumentArray().First(),
account.Number,
eventArgs.Method.Name));
}
private static void LogTransactionStarting(
MethodInvocationEventArgs eventArgs, Account account)
{
Console.WriteLine(string.Format(
"New {2} transaction on account {0}. Balance before operation is ${1}.",
account.Number,
account.CurrentBalance,
eventArgs.Method.Name));
}
private static void LogTransactionFinished(
MethodInvocationEventArgs eventArgs, Account account)
{
Console.WriteLine(string.Format(
"Finished {2} transaction on account {0}. Balance after the operation is ${1}.",
account.Number,
account.CurrentBalance,
eventArgs.Method.Name));
}
}
}
Notice that first we log the transaction start (line #17), then the transaction details, including parameters (line #18), only then we call the original method (line #19), and then, finally we log the transaction end (line #20). With this, we just replicated the original behavior using the two aspects, but using only the "on method invocation" =). Now, let’s get rid of our, now useless, aspect "on method boundary" on the account class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Codevil.MACSkeptic.PostSharpExample.Aspects;
namespace Codevil.MACSkeptic.PostSharpExample.Entities
{
[LogAccountTransactionDetails(
AttributeTargetMembers = "regex:(Deposit)|(Withdraw)")]
public class Account
{
public long Number { get; set; }
public string Owner { get; set; }
public decimal CurrentBalance { get { return this.currentBalance; } }
private decimal currentBalance;
private const decimal DefaultInitialBalance = 0;
public Account(long number, string owner)
: this(number, owner, DefaultInitialBalance)
{
}
public Account(long number, string owner, decimal initialBalance)
{
this.Number = number;
this.Owner = owner;
this.currentBalance = initialBalance;
}
public Account Deposit(decimal howMuch)
{
this.currentBalance += howMuch;
return this;
}
public Account Withdraw(decimal howMuch)
{
this.currentBalance -= howMuch;
return this;
}
}
}
Even in this small example you can see how we just got rid of, at the very least, 6 lines of alien code that were inside of our account class (6 lines considering the best case scenario using just OOP, if you moved the logging implementation to a service). These lines were strictly concerned with logging, which should be none of our account class concern. In their place, we had to add just a single line declaring an aspect of the behavior desired across our account class.
Just to keep things clear, my point here is not that less code is necessarily better (although it usually is), but that you should separate the responsibilities very clearly and write the code where it really belongs.
That’s all for now folks, I hope you’ve liked this and got a grasp on how powerful AOP can be. I strongly recommend that those interested in using aspects in real projects get to read carefully the PostSharp documentation to check the details about optimization and performance.
Like usual, the codebase for this article is on my github, and can be downloaded here. Feel free to leave your doubts, criticisms, suggestions and feelings at the comments.
Cheers, and see you next time ;).
References:
- PostSharp - main site: http://www.postsharp.org/
- PostSharp - register: http://www.postsharp.org/forum/ucp.php?mode=register
- PostSharp - documentation: http://doc.postsharp.org/1.5/
- Why Aspects/PostSharp: http://www.postsharp.org/about/video
Recommended Reading:
-
Importance of clear separation of concerns: https://mice.cs.columbia.edu/getTechreport.php?techreportID=438






