Advertisements
Posted by: jsonmez | October 12, 2010

A Simple Wrapper To Make Things More Fluent

This 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 having to go to a full blown AOP implementation.

Someone mentioned in the comments that it wasn’t very clear exactly what was going on with the final code.  I tend to agree that this:

LogOnError(_riceCooker.Cook);

… is not very clear.

Really, there are two problems with this code that I can see.

  1. It is not clear what this is going to do or whether or not LogOnError or Cook is the method we are concerned about.
  2. It’s not very self-discoverable at all.  If we had a library of useful wrapper methods like these, we wouldn’t have a good intellisense way to know what they are.

I can solve both of those issues, but doing so starts to move us into a weird zone where I am not quite sure I feel comfortable.  But, nevertheless, in the name of science…

wrapped

Let’s start backwards

Liking fluent interfaces, here is the kind of syntax that I would prefer to be able to use:

Wrapper.Wrap(_riceCooker.Cook).With.LogOnError();

It is a little bit longer syntax, but I like it for a few reasons:

  1. It clearly indicates what is going on here.  We are wrapping a method call using a wrapper.  We are wrapping with a method called LogOnError.
  2. You get intellisense all the way.  The correct implementation of this, should let me Type With + ‘.’ and then see a list of all the possible wrapping methods I have implemented.  This makes the wrapping set of methods self-discoverable.

I really like the idea of being able to easily change the functionality of the wrapping just by changing the last part of the line.  For example, if we had implemented a wrapping method that was LogAndAbortOnError(), we could change our code to use that pretty easily.

Wrapper.Wrap(_riceCooker.Cook).With.LogAndAbortOnError();

If we implement this correctly, intellisense will give us our options.

Making it so

Creating a fluent syntax in C# can often involve quite a bit of magic and voodoo.  I always like to gather my reagents before embarking on such a journey.

So grab a live chicken, a stapler, and a sharp knife and let’s go!

First step, let’s simplify this.  The With is nice, but it is just for flow, we don’t really need it.  So let’s figure out how to implement our syntax without the With and add it in afterwards.

Wrapper.Wrap(_riceCooker.Cook).LogOnError();

First the easy way.

  1. Create a static Wrapper class with a Wrap method that takes an Action and returns an Action.  (We’ll use this to convert whatever we pass in to an Action, so that we can use a Lambda expression or any method call there.)
  2. Create a static extension method that operates on an Action.  Call it LogOnError.
public static class Wrapper
{
    public static Action Wrap(Action action)
    {
        return action;
    }
        
    public static void LogOnError(this Action action)
    {
        try
        {
            action();
        }
        catch (Exception exception)
        {
            // Log the exception here
        }
    }
}

 

Not too bad.  Not a large amount of magic going on here.  Just using an extension method.

But, we already have a problem.  Using a plain old Action is going to give us too many choices in the intellisense drop down.  It could make it hard to know what our real options are and when we try and add the With syntax later, we will need to use a property off of an object we return from the Wrap method.

Making it better

We can fix this by actually wrapping the Action with a custom type that we can add our methods to.

Instead of Wrap returning an Action, it will return a WrappedAction.

public static class Wrapper
{
    public static WrappedAction Wrap(Action action)
    {
        return new WrappedAction() {Action = action};
    }
        
    public static void LogOnError(this WrappedAction wrappedAction)
    {
        try
        {
            wrappedAction.Action();
        }
        catch (Exception exception)
        {
            // Log the exception here
        }
    }

    public class WrappedAction
    {
        public Action Action { get; set; }
    }
}

Looking better.  Now when we put a ‘.’ at the end of our Wrap call we only see LogOnError as an option.

We can be sure now that if we create an extension method for a WrappedAction, we will make sure that method is self-discoverable.  Before, the generic Action extension  method could make our method show up places that we don’t want it to and can get lost in the other methods on Action.

Making it done

The last thing we need to do is add the With.

Ideally, when we hit the ‘.’ on the end of the Wrap method, we want to see With as an option.  When we hit the ‘.’ on the end of the With property, we want to see LogOnError as an option.

In order to accomplish this we need to:

  1. Add a With property to the WrappedAction.
  2. Have the With property be of a new type (WrappedActionTarget) so that we can add our extension methods for that new type.
  3. Change the extension method to operate on the new type.

Here is what we end up with:

public static class Wrapper
{
    public static WrappedAction Wrap(Action action)
    {
        return new WrappedAction() { Action = action };
    }

    public static void LogOnError(this WrapperMethodTarget target)
    {
        try
        {
            target.WrappedAction.Action();
        }
        catch (Exception)
        {
            Console.Write("Logging this error");
        }
    }
}

public class WrapperMethodTarget
{
    public WrappedAction WrappedAction { get; set; }
}

public class WrappedAction
{
    public Action Action { get; set; }

    public WrapperMethodTarget With
    {
        get
        {
            return new WrapperMethodTarget() { WrappedAction = this };
        }
    }
}

 

Now we can use the syntax of:

Wrapper.Wrap(_riceCooker.Cook).With.LogOnError();

We can move that LogOnError method out to another class, or create new extension methods somewhere else.  I just put it in there to avoid creating another class.

Is this really practical?

I don’t know.  To be honest, I was playing around with creating extension methods that work on Actions and I came up with this way to use them.

I could see making a wrapping library that had different kinds of ways you would wrap method calls built into it.  It could allow you to specify how you log in a configuration and then you would get all of this common stuff automatically.

Even if it is not practical, it’s pretty fun, and it demonstrates the power of Action, or rather functional programming in general.

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. […] A Simple Wrapper To Make Things More Fluent – John Sonmez continues looking at using wrapping methods with logging (or other cross cutting concerns) and explores creating a fluent-like interface for adding this functionality. […]

  2. Hey John,

    You don’t really need the wrapper class if you can get the Action to behave like one in the compiler. The easiest way to do that is to pass it to an identity function. I whipped up a quick example of that http://codepaste.net/78tf5e

    • You are correct, but then the extension method will apply to all actions.
      I was aiming for only having the extension methods show up on methods that used the wrapping syntax.
      But, yes if you don’t care about that it is a much simpler implementation. Thanks.

  3. Thought you might also like this where a chain of wrappers is built up and then executed: http://codepaste.net/mwiyyy

    • Ok, I really like that code. Very nice. I as trying to think of an implementation like that, but I didn’t come up with one as good as yours.
      That really makes it useful. By chaining all those actions as wrappers we can address multiple concerns for one methods.

      Thanks!

  4. I was wondering, is there a way we could do this with methods that contain varying parameters? Lets say we want to wrap a variety of method calls that take different parameters with standard logging, is that possible?

    So lets say we want to wrap all of the following method signatures:
    public bool FirstMethod(string someString)
    public void SecondMethod(bool someBool, int someInt)
    Or maybe something even more complex:
    public List ThirdMethod(SomeOtherObject firstObject, out SomeReturnObject secondObject)

    • Yeah, I believe all we would need to do is create an overload that uses Func instead of Action.

  5. You want to check AspectF.
    http://code.google.com/p/aspectf/
    This technique is extremely useful, I stopped using Postsharp ever since I learned this stuff.

    • Wow, that is so weird. I like my syntax a little better, but that project captures exactly what I was trying to do. Very nice! Thanks!


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: