Reputation: 6091
I'm trying to build a single "Or" predicate from a list of predicates in the form List<Expression<Func<T, bool>>>
public static IQueryable<T> Search<T>(this IQueryable<T> source, List<Expression<Func<T, bool>>> predicates = null)
where T : EntityObject
{
if (predicates == null || predicates.Count == 0)
return source;
else if (predicates.Count == 1)
return source.Where(predicates[0]);
else
{
var row = Expression.Parameter(typeof(T), "row");
var compoundExpression = predicates[0];
for (int i = 1; i < predicates.Count; i++)
{
compoundExpression = compoundExpression.Or(predicates[i]);
}
return source.Where(compoundExpression);
}
}
static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> lhs, Expression<Func<T, bool>> rhs)
{
var row = Expression.Parameter(typeof(T), "row");
var body = Expression.Or(
Expression.Invoke(lhs, row),
Expression.Invoke(rhs, row));
return Expression.Lambda<Func<T, bool>>(body, row);
}
But this is returning every row in my source?
For testing I am looking for c=>c.FullName.Contains("Smith") or c=>c.FullName.Contains("Jones")
I have tried amending to use PredicateBuilder but again it still returns every row in the source.
public static IQueryable<T> Search<T>(this IQueryable<T> source, List<Expression<Func<T, bool>>> predicates = null)
where T : EntityObject
{
if (predicates == null || predicates.Count == 0)
return source;
else if (predicates.Count == 1)
return source.Where(predicates[0]);
else
{
var pb = PredicateBuilder.False<T>();
for (int i = 0; i < predicates.Count; i++)
{
pb = pb.Or(predicates[i]);
}
return source.AsExpandable().Where(pb);
}
}
Any assistance very gratefully received!
The end result would be to allow AND's as well as OR's
e.g. c=>c.FullName.Contains("Dav") AND c=>c.CustomerType == 'Staff'
Upvotes: 1
Views: 1611
Reputation: 6091
Sometimes the weekend does strange things to code!
Not sure what I've done differently, but this is working:
public static IQueryable<T> Search<T>(this IQueryable<T> source, List<Expression<Func<T, bool>>> predicates = null)
where T : EntityObject
{
if (predicates == null || predicates.Count == 0)
return source;
else if (predicates.Count == 1)
return source.Where(predicates[0]);
else
{
var query = PredicateBuilder.False<T>();
foreach (var predicate in predicates)
{
query = query.Or(predicate);
}
return source.AsExpandable().Where(query);
}
}
PredicateBuilder appears to be a great little bit of code.
Upvotes: 1
Reputation: 531
Try
public static IQueryable<T> Search<T>(this IQueryable<T> source, IEnumerable<Expression<Func<T, bool>>> predicates = null)
where T : EntityObject
{
if (predicates == null || !predicates.Any())
return source;
else
{
ParameterExpression p = Expression.Parameter(typeof(T), "p");
Expression<Func<T,Bool>> predicate =
Expression.Lambda<Func<T,Bool>(
predicates.Select(l => ReParameteriser(l.Body, l.Paramaters[0], p)
.Aggregate((b1,b2) => Expression.Or(b1,b2)),
new ParamaterExpression[]{p});
return source.Where(predicate);
}
}
public class ReParameteriser : ExpressionVisitor
{
ParameterExpression originalParameter;
ParameterExpression newParameter;
private ReParameteriser(){}
protected ReParameteriser (ParameterExpression originalParameter, ParameterExpression newParameter)
{
this.originalParameter = originalParameter;
this.new = newParameter;
}
public static Expression ReParameterise(Expression expression, ParameterExpression originalParameter, ParameterExpression newParameter)
{
return new ReParameteriser(original,newParameter).Visit(expression);
}
protected override Expression VisitParameter(ParameterExpression node)
{
if (node == originalParameter)
return newParameter;
else
return node;
}
}
Note: The ExpressionVisitor class is .Net4 so if you want to target an earlier enviroment you will need to write your own. The code for this is only a google away, but the usual resource is Matt Warren's blog http://blogs.msdn.com/b/mattwar/archive/2008/11/18/linq-links.aspx
Upvotes: 1