Archive for the ‘AOP’ Tag
Ok, But why bother?
The standard arguments for using AOP apply – you can maintain separation of concern, add new behaviours, and all that.
The final reason for bothering was, in my case, a very slow and boring evening. Hey, it’s my blog and I don’t need to justify my choice of subjects, k?
The sources provided with this post, are not only bad for production use, but they are also fundamentally flawed as we shall discuss in a little while. If you intend to use them as they are, remember that I’m not taking any responsibility for them, am not likely to maintain them, etc etc. If you find a use for them and don’t mind the issues, you’re free to use them, play with them, or otherwise modify them as you see fit.
Since they are so conveniently available, it’s possible to pass them as arguments to another function, which is what we’re doing in the Aspect(target, aspect, implementation) function. In the first argument, target, we state which function we want to apply an aspect to. In the second argument, aspect, we specify the aspect we want to apply to that function – in other words, the behaviour that will be added to the target function. The third argument, implementation, is used to define the sequencing of the call. This is, again, a function, which calls target and aspect in a given order.
The Aspect function can be called through one of three helper functions: AspectBefore, AspectOnSuccess, and AspectOnException. These three define a specific sequencing implementation. AspectBefore, for example, calls the aspect before calling the target.
Arguments, arguments …
Although we are now able to attach additional behaviours to the function, we still can’t do anything useful with them, because we have no way of controlling them. Without the ability to pass arguments to them, aspects would be severely limited.
Unluckily, this vital item is probably the flakiest part of the entire script. I’m using the apply method, which allows us to invoke a function with an array of arguments. This function appears to be deprecated, which means that it may or may not be around in future. The real problem though, is with the scope. You see, the first parameter of the apply function defines the scope of the call – it tells the function what the “this” keyword will refer to. As it is, the scope is fudged, and may not refer to what you expected in many circumstances. This is the main reason for the great big warning above.
While this was just a simple excercise to pass the time (and is by no means complete), everyone is free to tinker the results. If anyone actually manages to make something useful out of it, do let me know! 😉
Recently, I came across PostSharp, an excellent open source framework for .net. Among the great set of toys which it brings along is LAOS, an Aspect Oriented Programming solution which works very neatly.
Aspect oriented programming is quite a tricky subject to explain in short, so I won’t. While it’s an oversimplification, for the purposes of this post, we will just say that it is a technique where you intercept the control flow as it passes from one block to another, and attach non-default behaviour at these points.
PostSharp provides a mechanism which lets you define attributes to specify this behaviour; you can then decorate an assembly, class, method or field with these attributes. The specific methods you overload will then be triggered accordingly.
As an example, we’re going to add a logger to a method. Yes, it’s pretty boring – 99.99% of the AOP discussions on the web use this scenario as an example. However, I just want to demonstrate how to use these attributes, so no need to rock the boat yet. (If you’re feeling adventurous, visit Manageability for a couple of ideas on what else you can do with AOP).
Let’s say that we want to write a log message every time that an exception is thrown from a method. Since we’re going to use this attribute to handle something that has to do with a method, we’re going to use the
OnMethodBoundaryAspect attribute as a base. Among others, this attribute exposes an OnException method, which is just what we need:
Like all interception methods exposed by OnMethodBoundaryAspect, OnException accepts a MethodExecutionEventArgs parameter. This gives us access to a lot of useful fields, among others, the name of the method being executed before interception (and all its reflected details), any parameters that were passed to the method, and the full details of the exception. This is especially handy when trying to re-create an exception.
Note the value being set in eventArgs.FlowBehavior. FlowBehavior.Continue tells the application to digest the exception and go on. Not, I must say, the smartest thing to do in all cases; in fact, I’d be dead set against finishing such an intercept this way in most cases. It’s been set that way for the purposes of the example application, where I want and expect an exception to be thrown half-way.
One other possible value of FlowBehaviour which I would like to mention briefly is the Return value. This makes the intercept digest the exception in the same way as Continue, but also allows us to set the MethodExecutionEventArgs.ReturnValue property. In this way, we can return a default value after an exception.
To actually make our new attribute DO something, we have to apply it to a method. We have a number of ways we can do this. If we want, we can specifically apply the attribute to a method (the parameter in the attribute is a constructor parameter for the attribute – refer to the source code attached):
We can also apply an attribute to all methods in a class:
Even more neatly, we can specify an expression that identifies a selection of methods within a class. For example,
The named property tells the attribute that it should apply to all methods and fields in the decorated class where the name starts with “MyLogged”.
Yet another option is to specify the attribute at an assembly level… but if you plan to do that, you really should move on to the actual PostSharp documentation 😛
The source code extends some more methods from the OnMethodBoundaryAspect attribute. You will need to install PostSharp to build the code.