views:

94

answers:

1

Hello,

I'm writing an Expression Parser to make my API more refactor friendly and less error prone. basicaly, I want the user to write code like that:

repository.Get(entity => entity.Id == 10);

instead of:

repository.Get<Entity>("Id", 10);

Extracting the member name from the left side of the binary expression was straight forward. The problems began when I tried to extract the value from the right side of the expression. The above snippet demonstrates the simplest possible case which involves a constant value but it can be much more complex involving closures and what not.

After playing with that for some time I gave up on trying to cover all the possible cases myself and decided to use the framework to do all the heavy lifting for me by compiling and executing the right side of the expression. the relevant part of the code looks like that:

public static KeyValuePair<string, object> Parse<T>(Expression<Func<T, bool>> expression)
{
    var binaryExpression = (BinaryExpression)expression.Body;

    string memberName = ParseMemberName(binaryExpression.Left);
    object value = ParseValue(binaryExpression.Right);

    return new KeyValuePair<string, object>(memberName, value);
}

private static object ParseValue(Expression expression)
{
    Expression conversionExpression = Expression.Convert(expression, typeof(object));
    var lambdaExpression = Expression.Lambda<Func<object>>(conversionExpression);
    Func<object> accessor = lambdaExpression.Compile();
    return accessor();
}

Now, I get an InvalidOperationException (Lambda parameter not in scope) in the Compile line. when I googled for the solution I came up with similar questions that involved building an expression by hand and not supplying all the pieces, or trying to rely on parameters having the same name and not the same reference. I don't think that this is the case here because I'm reusing the given expression.

EDIT

This is one of the nonworking scenarios:

ExpressionParser.Parse(entity => entity.InternalClass.Id == entity.Id);

I would appreciate if someone will give me some pointers on this. Thank you.

+1  A: 

The version with a constant works fine with the code as posted. Can you illustrate an example expression where it doesn't work?

When you see this, it means that your Right is trying to use a parameter; there is only one parameter in a Expression<Func<T, bool>> (the first one, of type T). For example, I would expect the following to break (and it does):

        // find people who are their own boss
        var pair = Parse<Foo>(entity => entity.Id == entity.ManagerId);

Re the complexity; there are a lot of cases that can be parsed without compile; I use a "try it and fallback to Compile" strategy. See the Evaluate and TryEvaluate methods, here.

Marc Gravell
Hey Marc, Thank you for the answer. this is exactly the type of expressions that I have problems with. your TryEvaluate fails too in the given scenario (please see the edited question). when I fall back to Compile, I get the mentioned exception.
Moshe Levi
@Moshe - well yes, it will. You *can't* do what you are asking unless you are passing in an instance at the same time. And it you are doing that, just compile it as a `Func<F, whatever>` and invoke the delegate passing in the instance. Where would you expect it to get the value from otherwise?
Marc Gravell