Saturday, July 31, 2010

CreateDelegate<T> – An Exercise in Using Expressions

In a previous blog post I showed a basic example of how to use the Delegate.CreateDelegate() method as an alternative to the slow MethodInfo.Invoke() for dynamically invoking a method of a class at runtime. The only downside of using CreateDelegate is that its not strongly typed. This is usually not a problem when the signature of the method that must be invoked is known at compile as is the case in the example shown in the blog post mentioned earlier.

var subject = new Subject();
var doSomething = (Func<String, String>)
    Delegate.CreateDelegate(typeof(Func<String, String>), subject, "DoSomething");
Console.WriteLine(doSomething("Hello Freggles"));

Here we’re simply able to cast the result to the requested delegate type. But what if a generic method must be invoked for which the type parameters can vary at runtime? You can still use the CreateDelegate method, but you can’t cast the result to a strongly typed delegate type. This means that in order to invoke the created delegate, the DynamicInvoke method must be called on the returned Delegate object. This has the nasty side effect that when the original method being called throws an exception, the DynamicInvoke method wraps the original exception in a TargetInvocationException.  

So in order to find a better way and also exercise my Expressions-fu, I tried to come up with a CreateDelegate<T> extension method that can be used to a create a strongly typed delegate for a MethodInfo object.

Suppose we have to dynamically invoke a method with the following signature:

private void Map<TDomainEvent, TEvent>(TDomainEvent domainEvent, TEvent @event)
    where TDomainEvent : IDomainEvent
    where TEvent : IEvent
{
    ...
}

Given an instance of IDomainEvent and IEvent, using the CreateDelegate<T> extension method that I’m about to show, we can dynamically invoke this method using the following code:

var action = GetType()
    .GetMethod("Map", methodBindings)
    .MakeGenericMethod(domainEvent.GetType(), @event.GetType())
    .CreateDelegate<Action<IDomainEvent, IEvent>>(this);

action(domainEvent, @event);

Here we determine a specific MethodInfo object for the Map method using the types of the event objects we have. Now here’s the code for the strongly typed CreateDelegate extension method.

public static class MethodInfoExtensions
{
    public static TDelegate CreateDelegate<TDelegate>(this MethodInfo method, 
                                                      Object instance) 
        where TDelegate : class
    {
        return CreateCachedDelegate<TDelegate>(method, 
            (typeArguments, parameterExpressions) =>
            {
                Expression<Func<Object>> instanceExpression = () => instance;
                return Expression.Call(Expression.Convert(instanceExpression.Body, 
                                                          instance.GetType()),
                                       method.Name,
                                       typeArguments,
                                       ProvideStrongArgumentsFor(method, 
                                                                 parameterExpressions));
            });
    }

    public static TDelegate CreateDelegate<TDelegate>(this MethodInfo method) 
        where TDelegate : class
    {
        return CreateCachedDelegate<TDelegate>(method, 
            (typeArguments, parameterExpressions) =>
                Expression.Call(method.DeclaringType, method.Name, typeArguments,
                                ProvideStrongArgumentsFor(method, parameterExpressions)));
    }

    private static TDelegate CreateCachedDelegate<TDelegate>(MethodBase method, 
        Func<Type[], ParameterExpression[], MethodCallExpression> getCallExpression)
        where TDelegate : class
    {
        var @delegate = GetFromCache<TDelegate>();
        if(null == @delegate)
        {
            @delegate = CreateDelegate<TDelegate>(method, getCallExpression);
            StoreInCache(@delegate);
        }

        return @delegate;
    }

    private static TDelegate GetFromCache<TDelegate>()
    {
        Object delegateObj;
        if(_delegateCache.TryGetValue(typeof(TDelegate), out delegateObj))
            return (TDelegate)delegateObj;

        return default(TDelegate);
    }

    private static void StoreInCache<TDelegate>(TDelegate @delegate)
    {
        _delegateCache.TryAdd(typeof(TDelegate), @delegate);
    }

    private static TDelegate CreateDelegate<TDelegate>(MethodBase method, 
        Func<Type[], ParameterExpression[], MethodCallExpression> getCallExpression)
    {
        var parameterExpressions = ExtractParameterExpressionsFrom<TDelegate>();
        CheckParameterCountsAreEqual(parameterExpressions, method.GetParameters());

        var call = getCallExpression(GetTypeArgumentsFor(method), parameterExpressions);

        var lambda = Expression.Lambda<TDelegate>(call, parameterExpressions);
        return lambda.Compile();
    }

    private static ParameterExpression[] ExtractParameterExpressionsFrom<TDelegate>()
    {
        return typeof(TDelegate)
            .GetMethod("Invoke")
            .GetParameters()
            .ToParameterExpressions()
            .ToArray();
    }

    private static void CheckParameterCountsAreEqual(
        IEnumerable<ParameterExpression> delegateParameters,
        IEnumerable<ParameterInfo> methodParameters)
    {
        if(delegateParameters.Count() != methodParameters.Count())
            throw new InvalidOperationException(
                "The number of parameters of the requested delegate does not match " +
                "the number parameters of the specified method.");
    }

    private static Type[] GetTypeArgumentsFor(MethodBase method)
    {
        var typeArguments = method.GetGenericArguments();
        return (typeArguments.Length > 0) ? typeArguments : null;
    }

    private static Expression[] ProvideStrongArgumentsFor(
        MethodInfo method, ParameterExpression[] parameterExpressions)
    {
        return method.GetParameters()
            .Select((parameter, index) => 
                Expression.Convert(parameterExpressions[index], 
                                   parameter.ParameterType))
            .ToArray();
    }

    private static readonly ConcurrentDictionary<Type, Object> _delegateCache =
        new ConcurrentDictionary<Type, Object>(); 
}

I actually provided two CreateDelegate methods here, one for instance methods (the first one) and one for static methods ( the second one). The created delegates are cached in a dictionary because the call of lambda.Compile() seems to be quite expensive.

Now I have to warn you that, based on first preliminary measurements, this implementation probably isn’t going to outperform  Delegate.CreateDelegate(). In fact, if you’re interested have a look at this approach first as it seems much faster. I added some spike code to the Elegant Code repository for those who fancy to take a look.  

Saturday, July 03, 2010

Using TransactionScope with SQLite

Earlier this week I ran into a little quirk with SQLite. Take a look at the following code snippet:

using(var transaction = new TransactionScope(TransactionScopeOption.Required))
{
    using(var connection1 = new SQLiteConnection(_connectionString))
    {
        connection1.Open();
        
        ... // Do stuff with the open connection
        
    } // Closes the connection (so we think …)
    
    using(var connection2 = new SQLiteConnection(_connectionString))
    {
        // This line of code throws a SQLiteException with an error 
        // code 'Busy' after a certain timeout has been expired
        connection2.Open();          
        
        ... 
    } 
}

Everything works just fine with the first connection, but the second connection forms a bigger problem. Calling the Open method on the second connection throws a SQLiteException with the message “The database file is locked” and an error code ‘Busy’ after a particular timeout has been expired.

At first I didn’t have a clue, largely because the TransactionScope was nicely tucked away at a much higher level (NServiceBus deals with transactions for me). But after reading more about the intrinsic behavior of SQLite it all started to make sense. Apparently SQLite supports only one writer at a time. The TransactionScope holds an exclusive writer lock, even when disposing the first connection. Because the first connection cannot fully close itself, opening the second connection results in a lock error.

Hope this helps explain why the obvious isn’t obvious :-).

Friday, July 02, 2010

Book Review: Switch - How to Change Things When Change is Hard

image

After hearing about this book in an interview with Mary and Tom Poppendieck, I decided to immediately purchase the audio version on Audible and listen to it during my daily commute to and from work. This book is all about how to enable and inspire change in all kinds of environments, be it at work, your local community or your private life.

The authors Chip Heath and Dan Heath make the advice to focus on the so-called “bright spots”. Most people, including myself, focus on flawed behavior and the things that go wrong. Instead, we should first be looking at the the things that go right, copy and do more of it in order to make change happen. Scaling up the “bright spots” instead of focusing on the negative parts.

In the book, change management is compared with an elephant with a rider on his back who are following a certain path.

The topic of change is therefore broken down in three simple sections:

  1. Direct the Rider - This represents the rational and logical part of the brain. This thoughtful part responds well to reason, facts and provides long-term thinking. In order to tell the rider what to do, just provide good arguments.
  2. Motivate the Elephant - This part of our brain responds to emotions and short-term gain. This is the biggest part and one where the Rider has limited control over, so both need to be convinced.
  3. Shape the Path - This completes the analogy made by the authors representing the Path which the Elephant and Rider are following.

When we’re faced in making a decision, we're often torn between our rational, logical reasons
and our emotional, intuitive feelings. The general consensus in the book is that if the Rider can direct the Elephant down a well prepared Path then there is a good chance for change. The authors filled the book with dozens of real-life, scientific and practical examples where these principles worked brilliantly (there’s even a story about BP and oil-drilling!!).

These are the nine steps that are illustrated by these anecdotal stories:

  1. Find the bright spots – Focus on the successes and not the failures.
  2. Script the critical moves – Remove decision paralysis by laying down small and easy steps.
  3. Point to the destination – Clearly describe and put out a goal that is hard to neglect and to which people can relate.
  4. Find the feeling – Try to find an emotional connection.
  5. Shrink the change – As agilists we all know the value of taking small steps. All big things are accomplished in tiny small steps. Change is no exception.
  6. Grow your people – Stimulate the “growth mindset” in people, helping to create a new identity to which people can relate.
  7. Tweak the environment – Make the necessary changes to peoples’ environment in order to point to the right direction.
  8. Build habits – Change peoples’' habits in order to change long-term behavior.
  9. Rally the herd – Use the power of group behavior.
    The first three steps are targeted at directing the Rider, the next three steps on motivating the Elephant and the last three steps on shaping the Path.
    You just need to read this book for yourself and utilize all the information that you’ve picked up from it. Just give it a try. Its a quick read (or listen) and afterwards, you’d be glad you did.