8

If you're adding "and" conditions to a Linq query, it's easy to do it like so:

var q = MyTable;
if (condition1)
  q = q.Where(t => t.Field1 == value1);
if (condition2)
  q = q.Where(t => t.Field2 > t.Field3);
// etc.

Is there any clever way of doing the same thing, when you want to add "or" conditions?

Shaul Behr
  • 36,951
  • 69
  • 249
  • 387
  • It involves expression-tree combining, and (if you are using EF) re-writing... It isn't necessarily pretty... But can be done if you really need... – Marc Gravell Jun 19 '11 at 09:53
  • @Marc Gravell - hmmm... define "not pretty"? I have a need to combine "or" clauses, but if it's going to be more work to make the "elegant" solution than it would be to do it by, say, making a distinct union query, then it may not be worth the effort. How difficult is it to do? – Shaul Behr Jun 19 '11 at 09:56
  • 1
    check this answer http://stackoverflow.com/questions/6383825/how-to-create-predicate-dynamically/6383937#6383937 – Eranga Jun 19 '11 at 10:05
  • Hey @Eranga, why not post that type of solution as an answer here? – Shaul Behr Jun 19 '11 at 10:20
  • answer with a different solution so you can choose which ever method you like – Eranga Jun 19 '11 at 10:45

5 Answers5

10

You can use a PredicateBuilder and use it to build an Or based expression:

 var predicate = PredicateBuilder.False<Product>();

 predicate = predicate.Or (t => t.Field1 == value1);
 predicate = predicate.Or (t => t.Field2 > t.Field3);

 q = q.Where (predicate);

You can read more about it here: http://www.albahari.com/nutshell/predicatebuilder.aspx

Replace the Product in PredicateBuilder.False<Product>() with your queried object.

Note that you start from a False predicate as you want to use Or. If You'd want an And predicate, Yuo should start from a True

Variant
  • 17,279
  • 4
  • 40
  • 65
4

Use the following:

var q = MyTable;
q = q.Where(
     t => (condition1 && t.Field1 == value1) || (condition2 && t.Field2 > t.Field3));
Akram Shahda
  • 14,655
  • 4
  • 45
  • 65
  • 2
    Thanks for offering help! Nice to have some goodwill passing over our international borders for a change... ;) – Shaul Behr Jun 19 '11 at 12:50
2

There is one way to do this that involves using expression trees. This way you build the Boolean expression yourself. It's pretty straight forward the tricky part though is that you need to rebase the parameters because otherwise it is going to refer the original lambda expression. See below for an example:

static void Main(string[] args)
{
    var source = new List<int> { 1, 2, 3 };

    var any = new List<Expression<Func<int, bool>>>();

    any.Add(x => x == 1);
    any.Add(x => x == 3);

    foreach (var item in source.AsQueryable().WhereDisjunction(any))
    {
        Console.WriteLine(item);
    }
}

class RewriteSingleParameterUsage : ExpressionVisitor
{
    public ParameterExpression Parameter { get; set; }

    protected override Expression VisitParameter(ParameterExpression node)
    {
        return Parameter;
    }
}

public static IQueryable<T> WhereDisjunction<T>(this IQueryable<T> source, IList<Expression<Func<T, bool>>> any)
{
    switch (any.Count)
    {
        case 0: return source;
        case 1: return source.Where(any[0]);
        default:
            var p = Expression.Parameter(any[0].Parameters[0].Type, any[0].Parameters[0].Name);
            var rw = new RewriteSingleParameterUsage { Parameter = p };
            var expr = rw.Visit(any[0].Body);
            for (int i = 1; i < any.Count; i++)
            {
                expr = Expression.Or(expr, rw.Visit(any[i].Body));
            }
            return source.Where(Expression.Lambda<Func<T, bool>>(expr, p));
    }
}

In the above example I'm being very harsh, I'm effectively replacing any parameter with this single new parameter that is being used to create the new expression. However, given the signature of this extension method it shouldn't really be possible to call this method with parameters such that it would cause an error. It is however going to be a problem if you involve more than one parameter.

John Leidegren
  • 59,920
  • 20
  • 131
  • 152
  • You've made a mistake somewhere in that boolean logic. Do you mean for there to be a `||` after `condition2`? Even so, it doesn't look right: if both condition1 and condition2 are true, then you've created an "and" condition between the two other expressions. @Akram's boolean expression is the correct one... – Shaul Behr Jun 19 '11 at 10:12
  • I did warn you that this was unintuitive. I see now that I've made a mistake never mind that then. – John Leidegren Jun 19 '11 at 10:38
  • I'll give you +1 for the effort, but I think the PredicateBuilder is the best and simplest solution on offer here. Thanks anyway! – Shaul Behr Jun 19 '11 at 12:48
  • @Shaul - while a library that does things for you, sometimes is nice, I think it's necessary to understand how those things work as well. The `PredicateBuilder` does more than create a compound disjunction for you, it also necessitates additional fluff, such as the `AsExpandable` operation. Those things are not free. – John Leidegren Jun 19 '11 at 19:45
2
var q = MyTable;
var conditions = new List<Func<T, bool>>();

 if (condition1)
     conditions.Add(t => ...);
 if (condition2)
     conditions.Add(t => ...);

 q.Where(x => conditions.Any(y => y(x)));
adrianm
  • 14,468
  • 5
  • 55
  • 102
  • Well, this isn't really LINQ as it is not based of IQueryable or expression trees so this cannot be translated to other execution environments. – John Leidegren Jun 19 '11 at 10:43
  • Yeah, true, it won't translate to SQL, which renders it pretty inefficient, at least for my purposes. – Shaul Behr Jun 19 '11 at 11:11
1

This is the same answer I gave here

As Marc Gravell said it involves expression-tree combining.

This article shows you how to do that. It takes a bit of work to initially set it up. But its worth it.

Alternate solution is to use Predicate Builder. The article does not explain very well what is actually happening under-the-hood. But the above article explains it nicely

Community
  • 1
  • 1
Eranga
  • 32,181
  • 5
  • 97
  • 96