简体   繁体   中英

Joining multiple where clauses in LINQ as OR instead of AND

Is there anyway to join LINQ where clauses as OR ?

var ints = new [] { 1, 3, 5, 7 };

var query = from i in ints select i;

query = query.Where (q => q == 3);

query = query..Where (q => q == 7);

What I want is the ability to dynamically add where clauses but make them use OR instead of AND

If you want to stay with your strong-typing Linq queries you should look into LinqKit and predicate building. I have used this for something similar and found it to work well with And / Or stacking of filters.

Check out the C#4.0/3.0 in a Nutshell excerpt for more in depth info. Here is a snip from my code:

        //Setup the initial predicate obj then stack on others:
        basePredicate = basePredicate.And(p => false);
        var predicate1 = PredicateBuilder.True<Person>();

        foreach (SearchParms parm in parms)
        {
            switch (parm.field)
            {
                case "firstname":
                    predicate1 = predicate1.And(p => p.FirstName.Trim().ToLower().Contains(sValue));
                    break;
                //etc...
            }

        }
        //Run a switch based on your and/or parm value to determine stacking:
        if (Parm.isAnd) {
             basePredicate = basePredicate.And(predicate1);
        } else {
             basePredicate = basePredicate.Or(predicate1);
        }

How about something like this?

var query = from i in ints where CheckConditions(i) select i;

public bool CheckConditions(int i)
{
    var conditions = WhereConditions; //an IEnumerable<Func<int, bool>> of  dynamically added conditions
    foreach (var condition in conditions)
    {
        if (condition(i)) return true;
    }
    return false;
}

You can probably expand this to be a bit cleverer but that's sort of how I'd do it.

EDIT: Sorry the first example was an AND, have changed it now to be an OR. So the first time it encounters a passing condition it returns true.

Using ExpressionVisitor to help to build the expression base on two expressions with OR/AND relationship. This answer is from Jeffery Zhao's blog .

internal class ParameterReplacer : ExpressionVisitor
{
    public ParameterReplacer(ParameterExpression paramExpr)
    {
        this.ParameterExpression = paramExpr;
    }

    public ParameterExpression ParameterExpression { get; private set; }

    public Expression Replace(Expression expr)
    {
        return this.Visit(expr);
    }

    protected override Expression VisitParameter(ParameterExpression p)
    {
        return this.ParameterExpression;
    }
}

public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> one, Expression<Func<T, bool>> another)
{
    var candidateExpr = Expression.Parameter(typeof(T), "candidate");
    var parameterReplacer = new ParameterReplacer(candidateExpr);

    var left = parameterReplacer.Replace(one.Body);
    var right = parameterReplacer.Replace(another.Body);
    var body = Expression.And(left, right);

    return Expression.Lambda<Func<T, bool>>(body, candidateExpr);
}

public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> one, Expression<Func<T, bool>> another)
{
    var candidateExpr = Expression.Parameter(typeof(T), "candidate");
    var parameterReplacer = new ParameterReplacer(candidateExpr);

    var left = parameterReplacer.Replace(one.Body);
    var right = parameterReplacer.Replace(another.Body);
    var body = Expression.Or(left, right);

    return Expression.Lambda<Func<T, bool>>(body, candidateExpr);
}

You can using Union method:

var ints = new [] { 1, 3, 5, 7 };
var query = ints.Where(q => q == 3);
query = query.Union(ints.Where(q => q == 7));

Are you talking about specifying more than one condition in the lambda?

query = query.Where(q => q == 3 ||
                         q == 7);

I am trying to do something similar. Here's what I came up with:

//various test cases
bool useTestCase1 = true;
bool useTestCase2 = true;
bool useTestCase3 = false;

query = query.Where(q => 
                      (q == 3 && useTestCase1 ) ||
                      (q == 7 && useTestCase2 ) ||
                      (q == 10 && useTestCase3 )
                    ); 

试试这个

var ints = new [] { 1, 3, 5, 7 };

var query = ints.select(X=>X).where(X=>X==3||X==7);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM