PostSharp is one of the leading products (not free) in the AOP (Aspect-oriented programming) space and Matthew Groves is of PostSharp. Mr. Groves spoke on AOP in general and how it can get better with PostSharp. I'm going to focus this blog posting on AOP in general though. So what is it? It has to do with cross-cutting concerns, with code that comes up over and over again, generally in a comparable syntactical shape, which you wish you could somehow keep in just one place in the name of the Don't Repeat Yourself rule and also in an attempt to minimize the spaghetti mess logic of sprinkling it about everywhere. Scattering and tangling comes with the spaghetti. Fortunately, there are ways to tease the cross-cutting concerns out to their own isolated implementations. Some examples of cross-cutting concerns are:
- logging
- caching
- null checks
- defensive programming (sanity checks)
One may use the Castle library to augment StructureMap wire-ups at Global.asax with some extra logic which empowers the aspects (code run when a method in the interface associated with an AOP-augmented StructureMap Dependency Injection wire-up is called AND code run either before or after the meat of the method is run). This is an example:
x.ForRequestedType<IFileIoRepository>()
.TheDefaultIsConcreteType
<FileIoRepository>()
.EnrichWith(i => proxyGenerator
.CreateInterfaceProxyWithTarget
<IFileIoRepository>(i,
new LoggingInterceptor()));
The actual example that Matthew gave in his talk looked more like this:
x.ForRequestedType<IFileIoRepository>().TheDefaultIsConcreteType<FileIoRepository>()
.EnrichWith(i => proxyGenerator.CreateInterfaceProxyWithTargetInterface
<IFileIoRepository>(i, new LoggingInterceptor()));
But I couldn't get it to work. Visual Studio put the proverbial squiggly red line under IFileIoRepository in the last place it appeared and suggested:
The non-generic method 'Castle.DynamicProxy.ProxyGenerator.CreateInterfaceProxyWithTargetInterface(System.Type, object, params Castle.Core.Interceptor.IInterceptor[])' cannot be used with type arguments
I eventually searched the web and found this which led me to the working implementation I offer first above. Anyways, LoggingInterceptor in my code is a class which I invented which implements IInterceptor and looks like so:
using AspectOriented.Core;
using Castle.Core.Interceptor;
using StructureMap;
namespace AspectOriented.UserInterface
{
public class LoggingInterceptor : IInterceptor
{
public void Intercept(IInvocation invocation)
{
ILoggingRepository loggingRepository =
ObjectFactory.GetInstance<ILoggingRepository>();
loggingRepository.Log("starting");
invocation.Proceed();
loggingRepository.Log("all done");
}
}
}
You can see I am logging before and after invocation.Proceed() which runs the thing being wrapped by the AOP logic, in this case some File I/O stuff. In Matthew's code he was also setting invocation.ReturnValue before invocation.Proceed() and then retrieving its value afterwards. (His example had to do with caching.) Beyond this, I am assuming there is other magic one may do with invocation in this circumstance to tell what is about to happen and thus how to react and to indeed react. There is likely a way to sniff the method signature of the method being called, etc. Anyways, the way PostSharp differs is that it lets one decorate methods with attributes. One rolls their own attribute that inherits from MethodInterceptionAspect which is a PostSharp class. This keeps the Global.asax StructureMap stuff from doing more than just Dependency Injection. Mine for example now has some extra complexity.
using System;
using AspectOriented.Core;
using AspectOriented.Infrastructure;
using Castle.DynamicProxy;
using StructureMap;
namespace AspectOriented.UserInterface
{
public class Global : System.Web.HttpApplication
{
protected void Application_Start(object sender, EventArgs e)
{
ObjectFactory.Initialize(x =>
{
var proxyGenerator = new ProxyGenerator();
x.ForRequestedType<IFileIoRepository>()
.TheDefaultIsConcreteType<FileIoRepository>().EnrichWith(i =>
proxyGenerator.CreateInterfaceProxyWithTarget
<IFileIoRepository>(i, new LoggingInterceptor()));
x.ForRequestedType<ILoggingRepository>()
.TheDefaultIsConcreteType<LoggingRepository>();
x.ForRequestedType<ITimeRepository>()
.TheDefaultIsConcreteType<TimeRepository>();
});
}
}
}
I saw John Crowe speak at Austin Code Camp too and he discouraged making a spaghetti mess inside one's Dependency Injection logic blob for associations. He didn't want extra concerns bleeding in here and thus I wonder if he would prefer the new PostSharp way of doing things to the Castle approach.
Depending on where you want to apply the aspects and what IoC tool you are using, it need not be a big mess :)
ReplyDeleteAlso, I just want to clarify that PostSharp Ultimate is not free, but PostSharp Starter Edition *is* free (you need a license key, but it's a free license key), and contains a core set of really good features.
Thanks Matthew!
ReplyDelete