0

I have an entity.

public class Foo
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Code { get; set; }
}

I want to create my own expression predicate. For that I have created a method that accepts property name and the value.

private static Expression<Func<Foo, bool>> Condition(string pName, object value)
{
    var pe = Expression.Parameter(typeof(Foo), "foo");
    var left = Expression.Property(pe, pName);
    var right = Expression.Constant(value);
    var equal = Expression.Equal(left, right);
    var predicate = Expression.Lambda<Func<Foo, bool>>(equal, pe);
    return predicate;
}

This is the predicate which works fine for a single condition.

using (var db = new MyEntities())
{
    var predicate = Condition("Name", "foo");
    var foos = db.Foos.Where(predicate).ToArray();
}

But when I tried to combine two conditions by following this post, it throws exception.

The parameter 'foo' was not bound in the specified LINQ to Entities query expression.

using (var db = new MyEntities())
{
    var cond1 = Condition("Name", "foo");
    var cond2 = Condition("Code", "bar");
    var body = Expression.AndAlso(cond1.Body, cond2.Body);
    var predicate = Expression.Lambda<Func<Foo,bool>>(body, cond1.Parameters[0]);
    var foos = db.Foos.Where(predicate).ToArray(); // exception
}

Please enlighten me.

Community
  • 1
  • 1
tsuta
  • 357
  • 4
  • 12

1 Answers1

3

The problem is that ParameterExpression in LINQ expressions is identified by reference equality, but the two Parameter objects are different references. (The name in ParameterExpression only exists for debugging purposes).

(If you reread the mentioned post, it says that the method that you tried would only work if both lambdas are defined on the same ParameterExpression object).

You have two big possibilities at this stage: either you define a way for the Condition function to accept a ParameterExpression object, or you create an ExpressionVisitor that will replace the original ParameterExpression with another. (Of course, given that you want to do an AndAlso, you could also conceivably chain two Where clauses, but that is less general.)

Jean Hominal
  • 16,518
  • 5
  • 56
  • 90
  • Thank you, so you are suggesting to take out the expression parameter as method parameter? I'll try that now. – tsuta Sep 15 '14 at 07:02
  • @tsuta: That is one way to do it. Another way would be to encapsulate the lambda expression building process with an object that will reuse the same parameter object everytime. Using an `ExpressionVisitor` is more complicated, but that would be the main way to do it if you do not control the `Expression` objects that are created. – Jean Hominal Sep 15 '14 at 07:07
  • Thank you for the explanation, I'm still learning expression and really interested how it builds the predicate, I'll try to use expression visitor later, but for now it covers my requirement because I have full control of my own expression. – tsuta Sep 15 '14 at 07:12