Owidat
Owidat

Reputation: 1081

how to call .Where() method in linq more than once to act like OR (||)


I'm trying to create a class that allow me to configure LINQ query at run time by using .Where() method, after completing my class and while testing result was not as I expected so I had an issue and to resolve that I needed to call multiple .Where() method times and it should act like OR || not like AND &&
Example:

class Program
{

    private static string WhereTest()
    {
        string result = "";

        // List
        string[] lst = { "Sa", "Su", "Mo", "Tu", "We", "Th", "Fr" };

        // test 1
        var q1 = from l in lst
                 where l.StartsWith("S") && l.EndsWith("u")
                 select l;
        result = "1st query count: " + q1.Count() 
            + "\n----------------- \nItems: \n------- \n";
        foreach (var item in q1.ToList())
        {
            result += item + "\n";
        }
        result += "\n";
        result += "\n";

        // test 2
        var q2 = from l in lst
                 where l.StartsWith("S") || l.EndsWith("u")
                 select l;
        result += "2nd query count: " + q2.Count() 
            + "\n----------------- \nItems: \n------- \n";
        foreach (var item in q2.ToList())
        {
            result += item + "\n";
        }
        result += "\n";
        result += "\n";

        // test 3
        var q3 = from l in lst
                 select l;
        if (true)
            q3 = q3.Where(l => l.StartsWith("S"));
        if (true)
            q3 = q3.Where(l => l.EndsWith("u"));

        result += "3rd query count: " + q3.Count() 
            + "\n----------------- \nItems: \n------- \n";
        foreach (var item in q3.ToList())
        {
            result += item + "\n";
        }
        result += "\n";
        result += "\n";


        return result;
    }
    static void Main(string[] args)
    {
        Console.WriteLine(WhereTest());
    }
}


Result:

            1st query count: 1
            -----------------
            Items:
            -------
            Su


            2nd query count: 3
            -----------------
            Items:
            -------
            Sa
            Su
            Tu


            3rd query count: 1
            -----------------
            Items:
            -------
            Su

In my example q3 return the same result of q1
So I need to know if I can make q3 return me the same reult of q2.
Regards

Upvotes: 1

Views: 971

Answers (3)

Kirill Polishchuk
Kirill Polishchuk

Reputation: 56182

Look at System.Linq.Expressions

// List
string[] lst = { "Sa", "Su", "Mo", "Tu", "We", "Th", "Fr" };

Expression<Func<string, bool>> expr1 = t => t.StartsWith("S");
Expression<Func<string, bool>> expr2 = t => t.EndsWith("u");

var filter = new List<Expression>();

if (true)
{
    filter.Add(expr1);
}

if (true)
{
    filter.Add(expr2);
}

var input = Expression.Parameter(typeof(string), "input");
var result = Expression.Parameter(typeof(bool), "result");

var blockExpression = Expression.Block(
    new[] { result },
    Expression.Assign(result, Expression.Or(Expression.Invoke(expr1, input), Expression.Invoke(expr2, input)))

    );

var x = Expression.Lambda<Func<string, bool>>(blockExpression, new[] { input });

var func = x.Compile();

var res = lst.Where(func);


foreach (var re in res)
{
    Console.WriteLine(re);
}

Upvotes: 2

Jon Skeet
Jon Skeet

Reputation: 1502376

For LINQ to SQL or other queryable-based providers, PredicateBuilder is the solution. It doesn't let you call Where multiple times, but it lets you create a predicate dynamically using multiple calls.

For LINQ to Objects, you can create your own equivalent of PredicateBuilder using delegates instead of expression trees, as shown in this answer. Just to make this answer complete in its own right, here's the same code again:

public static class DelegatePredicateBuilder
{
  public static Func<T, bool> True<T>()  { return f => true;  }
  public static Func<T, bool> False<T>() { return f => false; }

  public static Func<T, bool> Or<T>(this Func<T, bool> expr1,
                                     Func<T, bool> expr2)
  {
      return t => expr1(t) || expr2(t);
  }

  public static Func<T, bool> And<T>(this Func<T, bool> expr1,
                                     Func<T, bool> expr2)
  {
      return t => expr1(t) && expr2(t);
  }
}

So you'd use something like:

var predicate = DelegatePredicateBuilder.False<string>();

if (someCondition)
{
    predicate = predicate.Or(l => l.StartsWith("S"));
}

if (someCondition)
{
    predicate = predicate.Or(l => l.EndsWith("u"));
}

query = query.Where(predicate);

Upvotes: 2

dotnetstep
dotnetstep

Reputation: 17485

you can do something like this.

q3 = q3.Where(l => l.EndsWith("u") || l => l.StartsWith("S")); 

If you looking for dynamic way of building query following link has predicate builder that help you.

http://www.albahari.com/nutshell/predicatebuilder.aspx

Upvotes: 1

Related Questions