Using AOP and PostSharp to Enhance Your Code: Part A

January 29, 2007

10 comments

Click here for Part B

I’ve been looking at ways to improve the quality of my team’s code by removing “unrelated” code from within methods. By that I mean things like opening transactions, caching and exception handling.

For instance, code like that might be quite common:

public object GetSomething() { try { object o = HttpContext.Current.Cache["MySomething"]; if (o == null) return o; using (TransactionScope scope = new TransactionScope()) { o = DataAccess.ActuallyGetSomething(); scope.Complete(); } HttpContext.Current.Cache["MySomething"] = o; return o; } catch (Exception x) { ExcptionPolicy.HandleException(x, "MyPolicy"); } }

As you can see, it might look like this code does a lot of things, but actually it’s only data access wrapped with all these really required wrappers: transactions, caching, exceptions. And if I wanted to add logging in the beginning and end of every method there will be hell to pay…

Well, In reality it’s not really that bad. The wrapping code is spread over several layers, and we’re also using an anonymous method mechanism to reduce copy-pasting of code, but still. I want to get this ugliness out of my code.

This is what I want:

[EnsureTransaction] [HandleException] [Cached("MySomething")] public object GetSomething() { return DataAccess.ActuallyGetSomething(); }

In order to reach this attributed heaven I knew I would probably have to use a technique I heard a lot about but never actually tried to use – Aspect Oriented Programming, or AOP. Among other things, AOP allows you to inject advice (some code or behavior) at join-points – locations in your code, like at beginning or end of a method. And it allows you to do this in a cross-cutting manner, which means you don’t have to write any more code inside your method to make it work.

So I went to look for a .NET AOP framework that we might be able to use. I admit I didn’t invest that much time in searching and checking out the different options (and believe me, there’s plenty). I went for the first framework I found that allows me to write my beloved attributes as quickly as possible and to easily check if the damn thing even works.

The framework I finally found is called PostSharp, which is a post compiler for .NET. What’s a post compiler? You might ask. Well, basically, a .NET post compiler lets you compile your code into an assembly, and then it changes your IL code. Freaky, eh? PostSharp achieves this by using some MS-Build tasks that hunts, among other things, these special attributes and replaces them with code. What kind of code? Well, whatever you tell it to.

Finally, I was able to implement the attributes I wanted. There’s one:

[Serializable] [global::System.AttributeUsage(AttributeTargets.All, Inherited = false, AllowMultiple = true)] public sealed class CachedAttribute : OnMethodInvocationAspect { private readonly string _cacheKey; public CachedAttribute(string cacheKey) { this._cacheKey = cacheKey; } public string CacheKey { get { return this._cacheKey; } } public override void OnInvocation(MethodInvocationEventArgs eventArgs) { object cached = HttpContext.Current.Cache[CacheKey]; if (cached == null) { cached = eventArgs.Delegate.DynamicInvoke(eventArgs.GetArguments()); HttpContext.Current.Cache[CacheKey] = cached; } eventArgs.ReturnValue = cached; } }

What you can see here, is an attribute that inherit’s from PostSharp’s OnMethodInvocationAspect base class. Any call to a method marked with my cached attribute will be replaced to a call to a different method, which first checks for the specified CacheKey (look at the OnInvocation method – the methodInstance parameter is a delegate to the wrapped method).

And there’s another one:

[Serializable] [global::System.AttributeUsage(AttributeTargets.Method, Inherited = true, AllowMultiple = false)] private sealed class HandleExceptionAttribute : OnMethodBoundaryAspect { public override void OnEntry(MethodExecutionEventArgs eventArgs) { base.OnEntry(eventArgs); } public override void OnExit(MethodExecutionEventArgs eventArgs) { base.OnExit(eventArgs); } public override void OnException(MethodExecutionEventArgs eventArgs) { base.OnException(eventArgs); ExceptionPolicy.HandleException(eventArgs.Exception, "General"); } }

This is even simpler: Using the OnMethodBoundaryAspect, I can inject code at the beginning and end of the method. Here I’m using this to handle exceptions (using Enterprise Library ExceptionPolicy object), but I can use this for transactions/logging/whatever. Excuse me for not implementing EnsureTransactionAttribute for now, but I will leave that for the reader :)

And voila:

[HandleException] [Cached("MySomething")] public object GetSomething() { return DataAccess.ActuallyGetSomething(); }

In order to make all this work, you have to:

1. Download and install PostSharp. You should download the latest build from the daily builds page.

2. The install will modify your standard build process (more on that in Part B), so you will have to define the constant POSTSHARP in your project properties in VisualStudio, in order to tell the Post-Compiler to kick in and do the dirty stuff. (Project properties -> Build -> General -> Conditional Compilation Symbols).

3. And of course, you have to reference PostSharp’s DLLs (specifically – PostSharp.Laos, PostSharp.Public) which are automatically installed in your GAC.

And that’s it! Just use these attributes on your methods, compile (yeah, the post-compiling is gonna make this a bit longer) and see how the magic works.

Hope you enjoyed this. In part B we’ll talk more about how this whole thing works, I’ll show you some more tricks you can use, talk about some pitfalls you might want to avoid and conclude with the advantages and disadvantages of using a post compiler (That is, of course, if I won’t change my mind by the time I write the second part and decide that I want to talk about flowers instead. Or something).

Good night, and have a pleasant tomorrow!

Updated 2.2.2007: changed to use a newer build of PostSharp.

Add comment
facebook linkedin twitter email

Leave a Reply

Your email address will not be published.

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

10 comments

  1. ChenFebruary 2, 2009 ב 16:39

    Hi Doron,

    Thanks alot for the great article !!!
    Did you check the performance issues?

    Thanks,
    Chen

    Reply
  2. sclschJanuary 30, 2010 ב 5:18

    不错啊,我做权限控制正需要这个。

    Reply
  3. Steve-OJuly 28, 2010 ב 20:22

    Funtastic! I was basically in the same position as you, came to the same conclusions but was having a wee bit of trouble ‘putting it all together’. Thanks a bunch!

    Reply
  4. fantasyApril 1, 2011 ב 15:50

    And we still have very cold ((((

    Reply
  5. remonlineApril 4, 2011 ב 11:27

    Why I can not print a document?

    Reply
  6. AlldramasApril 6, 2011 ב 20:50

    Had already seen something like this

    Reply
  7. litrarefApril 11, 2011 ב 10:09

    Why I can not print a document?

    Reply
  8. Paul RussoSeptember 7, 2011 ב 19:00

    A good, to the point, introduction to AOP using PostSharp. Now to convince the technical architects in our team!

    Reply
  9. taobaowangnvzhuangniuzishangyi September 12, 2011 ב 18:00

    。!!zhen bang !!!!!! Gr888zuo ye …

    Reply
  10. 淘宝网女装秋装皮衣 September 15, 2011 ב 12:47

    yi yu li jie de ,wo xi huan ta !

    Reply