Rails dynamic finders for .NET 4.0

Ruby on Rails allows you to use ‘dynamic’ finders to query the database. This is actually a feature from ActiveRecord to dynamicly use methods which will represent where clauses on the database.

Some examples:

User.find(:first, :conditions => ["name = ?", name])

User.find(:all, :conditions => ["city = ?", city])

User.find(:all, :conditions => ["street = ? AND city IN (?)", street, cities])
User.find_all_by_street_and_city(street, cities)

With .NET 4.0 we have dynamics of our own so I thought why not recreate this feature…

By creating an extension method for IEnumerable and IQueryable each enumerable source can now use the dynamic finder feature

public static class DynamicExtensions
    public static dynamic AsDynamic<T>(this IEnumerable<T> source)
        return new DynamicEnumerable<T>(source);

    public static dynamic AsDynamic<T>(this IQueryable<T> source)
        return new DynamicQueryable<T>(source);

The Dynamic classes will inherit from DynamicObject to give basic dynamics support.

sealed class DynamicQueryable<T> : DynamicObject
    private readonly IQueryable<T> source;

    public DynamicQueryable(IQueryable<T> source)
        this.source = source;

    public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
        var match = methodMatcher.Match(binder.Name);
        if (match.Success)
            var properties = match.Groups[2].Value.Split(new[] { "And" }, StringSplitOptions.RemoveEmptyEntries);
            var predicate = BuildExpression<T>(properties, args);

            if (match.Groups[1].Success)
                result = source.Where(predicate);
                result = source.FirstOrDefault(predicate);

            return true;
        return base.TryInvokeMember(binder, args, out result);

With the only missing part the BuildExpression method which will create the expression tree:

private static Expression<Func<T, bool>> BuildExpression<T>(string[] properties, object[] args)
    if (properties.Length < 1)
       throw new InvalidOperationException("Need to specify at least one property.");
    if (args.Length != properties.Length)
       throw new InvalidOperationException("Method expects " + properties.Length + " parameters and only got " + args.Length + " values.");

    var param = Expression.Parameter(typeof(T), "p");
    var body = Expression.Equal(Expression.Property(param, properties[0]), Expression.Constant(args[0]));
    for (var i = 1; i < properties.Length; i++)
        body = Expression.AndAlso(body, Expression.Equal(Expression.Property(param, properties[i]), Expression.Constant(args[i])));
    return Expression.Lambda<Func<T, bool>>(body, param);

And the only difference with the DynamicEnumerable class is that the expression will be compiled to use for the Where/FirstOrDefault.

Full source at pastebin.

Written Edit