Advertisements
Posted by: jsonmez | October 9, 2010

Aspect Oriented Programming with Action<>

Aspect Oriented Programming (AOP) is a pretty great concept.

It is a little difficult to implement though.

To be honest, I don’t think I’ve ever really seen it successfully implemented.  I mean sure, I’ve seen examples of how you could use it for “cross-cutting” concerns like logging.

Mad_scientist

The problem is it is usually pretty difficult to use, and the only real practical application I can ever come up with is logging.  I know, it is probably just my lack of knowledge in the area, but if you bear with me I’ll show you a neat little trick you can use to address cross-cutting concerns by doing something similar to what AOP does using Action<>.

Putting it all in one place

The main problem AOP tries to solve is taking aspects of your software that exist in many different places and condensing them into one place for you to maintain.

Exception handling and logging tend to be the most infamous of these cross-cutting concerns.  Many places in your code you no doubt have many instances where you catch an exception and the only thing you can really do is log it.

I’m going to show you a little easy way to do that using Action<>.

Giving credit where credit is due, I got this idea from some code that a coworker of mine, Subha Tarafdar, wrote.  (He is a genius.)

He wrote some code to basically do what I am going to show you, but with retrying database queries.  He was able to reduce many places in the code base where we had repeated logic to retry executing database queries when getting a deadlock or timeout.

Does this code belong to you?

public void MakeRice()
{
    try
    {
        _riceCooker.Cook();
    }
    catch (Exception exception)
    {
        // Don't care if this fails,
        // there is nothing we can do about it.
        Logger.Log(exception.Message);
    }
}

 

Ignore that I am catching a general exception here.  It is a bad practice, but sometimes all you are going to do is log whatever bad thing happens and move on.

It’s pretty common to do something in a try block and catch an exception only to log it.

Think about how many times this code or something similar to it might be sprinkled throughout your code base.

Action<> to the rescue

If you’re not familiar with Action<> take a look at this post I did that gives a very simple explanation for how it works.

We can take the logic of the try, catch, and log exceptions and put it into a method that only varies by what action we do.

public static void LogOnFailure(Action action)
{
    try
    {
        action();
    }
    catch (Exception exception)
    {
        Logger.Log(exception.Message);
    }
}

 

I’m not a big fan of static methods but in certain cases they make sense.  The alternative is to have all of this code sprinkled throughout your code base.

Now that we have this method, we can do anything we want and know that if there is an error it will be logged.

Check this out:

LogOnFailure(_riceCooker.Cook);
LogOnFailure(KickACat);
LogOnFailure(() =>
                    {
                        Wakeup();
                        SmellTheRoses();
                    }
            );

 

And if you decide you want to change how you log the error or what you do on it, you can change it all in one place.

Not just for logging

You can apply this kind of solution in many places where you have cross cutting concerns in your code base.

Here are a few suggestions for places you might consider this kind of a solution:

  • Retrying on failure logic
  • Using an alternative service for a failure (web service “A” failed, but we can use web service “B”)
  • Database connection and connection closing logic.  (Open connection, do something, close connection.)
As always, you can subscribe to this RSS feed to follow my posts on Making the Complex Simple.  Feel free to check out ElegantCode.com where I post about the topic of writing elegant code about once a week.  Also, you can follow me on twitter here.
Advertisements

Responses

  1. You’re passing an action into LogOnFailure(). what if the the action passes – isn’t that a bit odd to pass it into the LogOnFailure() method.

    Maybe its me looking at it the wrong way, or maybe its just bad naming.

    An interesting post, though.

    • I initially also read the method name as “logon failure”, but what is actually meant is “log on failure”.

      I’ve used this technique in the past too, and one of the most difficult parts is actually finding a good name for these methods 🙂

      • haha

  2. A coworker of mine pointed out that there was an error in the code demonstrated above.

    if ricecooker.Cook() is a function, than you are passing the result in to LogOnFailure. The correct code would have been

    LogOnFailure(_riceCooker.Cook);

    There is also a downside to this method which is that you can’t pass parameters (unless you wrap it of course).

    For the rest it is a cool method.

    • You are correct, thanks for pointing that out. Totally missed that the first time around. Fixed now.

    • You can use Func instead of Action if you need params also.

      • Or extra overloads for the generic types

        public static void LogOnFailure(Action action, T arg)
        {
        try
        {
        action(arg);
        }
        catch (Exception exception)
        {
        Logger.Log(exception.Message);
        }
        }

      • That should have been with generic types <T>

  3. […] Aspect Oriented Programming with Action<> – John Sommez talks about AOP and how it can help deal with cross cutting concerns, and looks at how Action<> can provide a similar means of managing cross cutting concerns. […]

  4. LogOnFailure(_riceCooker.Cook());
    LogOnFailure(KickACat());

    should be

    LogOnFailure(_riceCooker.Cook);
    LogOnFailure(KickACat);

  5. Interesting idea, but now your code is dominated by a secondary concern, logging if something goes wrong. Maybe its just me, but it makes it hard to read and gives the impression that LogOnFailure is somehow the important part.

  6. I suggest you to take a look at the NCrawler project on codeplex. There is a nice implementation of AOP imo.

  7. […] post is really a continuation from my last post on using a method that takes an Action to address cross cutting concerns, like logging, without […]


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Categories

%d bloggers like this: