tags:

views:

127

answers:

1

I followed this thread: link text

Jason gives an example:

   public static Expression<TDelegate> AndAlso<TDelegate>(this Expression<TDelegate> left, Expression<TDelegate> right)
{
    return Expression.Lambda<TDelegate>(Expression.AndAlso(left, right), left.Parameters);
}

and its usage as such:

Expression<Func<Client, bool>> clientWhere = c => true;
if (filterByClientFName)
{
    clientWhere = clientWhere.AndAlso(c => c.ClientFName == searchForClientFName);
}
if (filterByClientLName)
{
    clientWhere = clientWhere.AndAlso(c => c.ClientLName == searchForClientLName);
}

I have a orders table and i followed the above example, changing column names, and i get the similar error that the post creator had

The binary operator AndAlso is not defined for the types 'System.Func`2[Models.Order,System.Boolean]' and 'System.Func`2[Models.Order,System.Boolean]'.

Anyone have any thoughts on what I am missing?

UPDATED: Eric, I further followed what the user of the previous post was asking, here

[link text][2]

The user has this

Expression<Func<Client, bool>> clientWhere = c => true;
Expression<Func<Order, bool>> orderWhere = o => true;
Expression<Func<Product, bool>> productWhere = p => true;

if (filterByClient)
{
    clientWhere = c => c.ClientID == searchForClientID;
}

Now if he were to have various conditions in filterByClient, say he either has clientid and/or some other column name, how would one build the clientWhere expression?

+6  A: 

You're attempting to build an expression tree that represents this:

c => true && c.ClientFName == searchForClientFName

You are actually building an expression tree that represents this:

c => c=> true && c => c.ClientFName == searchForClientFName

which makes no sense at all.

Now, you might naively think that this will work:

public static Expression<TDelegate> AndAlso<TDelegate>(this Expression<TDelegate> left, Expression<TDelegate> right) 
{ 
// NOTICE: Combining BODIES:
    return Expression.Lambda<TDelegate>(Expression.AndAlso(left.Body, right.Body), left.Parameters); 
} 

That would produce in your case something representing

c => true && c.ClientFName == searchForClientFName

Which looks right. But in fact this is fragile. Suppose you had

... d => d.City == "London" ...
... c => c.ClientName == "Fred Smith" ...

and you used this method to combine them. You'd get an object representing

c => d.City == "London" && c.ClientName == "Fred Smith"

What the heck is d doing in there?

Furthermore, parameters are matched by object identity, not by parameter name. If you do this

... c => c.City == "London" ...
... c => c.ClientName == "Fred Smith" ...

and combine them into

c => c.City == "London" && c.ClientName == "Fred Smith"

you're in the same boat; the "c" in "c.City" is a different c than the other two.

What you actually need to do is make a third parameter object, substitute it in the bodies of both lambdas for every occurence of their parameters, and then build up a new lambda expression tree from the resulting substituted bodies.

You can build a substitution engine by writing a visitor that passes over the expression tree body, rewriting it as it goes.

Eric Lippert