I just watched this talk by Greg Young warning people to KISS: Keep It Simple Stupid.
One of the things he suggested is that to do aspect-oriented programming, one does not need a framework.
He starts by making a strong constraint: that all methods take one, and only one, parameter (though he relaxes this a little later by using partial application).
The example he gives is to define an interface:
public interface IConsumes<T>
{
void Consume(T message);
}
If we want to issue a command:
public class Command
{
public string SomeInformation;
public int ID;
public override string ToString()
{
return ID + " : " + SomeInformation + Environment.NewLine;
}
}
The command is implemented as:
public class CommandService : IConsumes<Command>
{
private IConsumes<Command> _next;
public CommandService(IConsumes<Command> cmd = null)
{
_next = cmd;
}
public void Consume(Command message)
{
Console.WriteLine("Command complete!");
if (_next != null)
_next.Consume(message);
}
}
To do logging to console, one then just implements:
public class Logger<T> : IConsumes<T>
{
private readonly IConsumes<T> _next;
public Logger(IConsumes<T> next)
{
_next = next;
}
public void Consume(T message)
{
Log(message);
if (_next != null)
_next.Consume(message);
}
private void Log(T message)
{
Console.WriteLine(message);
}
}
Then, the pre-command logging, command service, and post-command logging are then just:
var log1 = new Logger<Command>(null);
var svr = new CommandService(log);
var startOfChain = new Logger<Command>(svr);
and the command is executed by:
var cmd = new Command();
startOfChain.Consume(cmd);
To do this in, for example, PostSharp, one would annotate the CommandService
this way:
public class CommandService : IConsumes<Command>
{
[Trace]
public void Consume(Command message)
{
Console.WriteLine("Command complete!");
}
}
And then have to implement the logging in an attribute class something like:
[Serializable]
public class TraceAttribute : OnMethodBoundaryAspect
{
public override void OnEntry( MethodExecutionArgs args )
{
Console.WriteLine(args.Method.Name + " : Entered!" );
}
public override void OnSuccess( MethodExecutionArgs args )
{
Console.WriteLine(args.Method.Name + " : Exited!" );
}
public override void OnException( MethodExecutionArgs args )
{
Console.WriteLine(args.Method.Name + " : EX : " + args.Exception.Message );
}
}
The argument Greg uses is that the connection from the attribute to the implementation of the attribute is "too much magic" to be able to explain what's happening to a junior developer. The initial example is all "just code" and easily explained.
So, after that rather longwinded build-up, the question is: when do you make the switch from Greg's non-framework approach to using something like PostSharp for AOP?
IConsumes
pieces. Rather than having to use external XML or some Fluent interface --- yet another thing to learn. One could argue that this methodology is "another thing to learn" also.