I've got a form with multiple fields on it (company name, postcode, etc.) which allows a user to search for companies in a database. If the user enters values in more than one field then I need to search on all of those fields. I am using LINQ to query the database.
So far I have managed to write a function which will look at their input and turn it into a List of expressions. I now want to turn that List into a single expression which I can then execute via the LINQ provider.
My initial attempt was as follows
private Expression<Func<Company, bool>> Combine(IList<Expression<Func<Company, bool>>> expressions) { if (expressions.Count == 0) { return null; } if (expressions.Count == 1) { return expressions[0]; } Expression<Func<Company, bool>> combined = expressions[0]; expressions.Skip(1).ToList().ForEach(expr => combined = Expression.And(combined, expr)); return combined; }
However this fails with an exception message along the lines of "The binary operator And is not defined for...". Does anyone have any ideas what I need to do to combine these expressions?
EDIT: Corrected the line where I had forgotten to assign the result of and'ing the expressions together to a variable. Thanks for pointing that out folks.
You can use Enumerable.Aggregate
combined with Expression.AndAlso
. Here's a generic version:
Expression<Func<T, bool>> AndAll<T>( IEnumerable<Expression<Func<T, bool>>> expressions) { if(expressions == null) { throw new ArgumentNullException("expressions"); } if(expressions.Count() == 0) { return t => true; } Type delegateType = typeof(Func<,>) .GetGenericTypeDefinition() .MakeGenericType(new[] { typeof(T), typeof(bool) } ); var combined = expressions .Cast<Expression>() .Aggregate((e1, e2) => Expression.AndAlso(e1, e2)); return (Expression<Func<T,bool>>)Expression.Lambda(delegateType, combined); }
Your current code is never assigning to combined
:
expr => Expression.And(combined, expr);
returns a new Expression
that is the result of bitwise anding combined
and expr
but it does not mutate combined
.
EDIT: Jason's answer is now fuller than mine was in terms of the expression tree stuff, so I've removed that bit. However, I wanted to leave this:
I assume you're using these for a Where
clause... why not just call Where with each expression in turn? That should have the same effect:
var query = ...; foreach (var condition in conditions) { query = query.Where(condition); }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With