简体   繁体   中英

How to generate an AND with a grouped OR query, with LINQ?

I'm struggling to understand how to come up with a query like the following, using LINQ ...

SELECT *
FROM foo
WHERE a = false AND (b = true OR c = true)

I'm using Entity Framework and LINQ to query my database. I've got a table and entity class that has some boolean columns, and looks like so ...

public class Foo
{
  public bool A { get; set; }
  public bool B { get; set; }
  public bool C { get; set; }
}

var foos = DbSet<Foo>();

I'm trying to write a query that will select these records in such a way that if I want to include columns that are true, they need to be OR'd together, not AND'd. For the properties that are not true, though, they need to exist as ANDs.

I'm sure this is something very basic but I'm just failing to see the simple solution. Specifically, I cannot figure out how to progressively add the OR clauses.

Edit:

To clarify my logic, I'll explain what I'm trying to make happen. I'm trying to write a filter query that would allow me to ask the database, "show me all Foos where either A is true, or B is true and C is not true". Or maybe the query would need to ask, "show me all Foos where C is true and A and B are not true".

This is a query to be used as part of an ASP.NET action method, filtering based on some check boxes. So, the action method knows whether A, B or C need to be filtered as true or false, I just need to write a LINQ expression that builds this query.

LINQ与SQL几乎相同:

foos.Where(n => n.A == false && (n.B == true || n.C == true))
var result = foos.Where(x => !x.A && (x.B || a.C)).ToList();

If A is true, it needs to become part of that OR expression, otherwise it needs to be part of the AND expression

You can use an ? operator.

var result = foos.Where (x => x.A ? x.B && x.C : x.B || x.C);

So if xA == true statement will be xB && xC , or xB || xC xB || xC otherwise

I disagree with buffjape. In your case I would progressively add Where clauses. I would do something like the following:

//start with the full set
var results = DbSet<Foo>();

//then build the where clause using conditional logic based on which checkboxes are checked

if(aCheckBoxIsChecked)
{
    results = results.Where(foo => foo.A == true); //each where you add is effectively an and
}

if(bCheckBoxIsChecked)
{
    results = results.Where(foo => foo.B == true);
}

... //and keep on with as many checkboxes as you have


if(aCheckBoxIsChecked && cCheckBoxIsChecked)
{
    results = results.Where(foo => foo.B != true);
}
else if(!aCheckBoxIsChecked)
{
    results = results.Where(foo => foo.B == true || foo.C == true);
}
else if(...) //any other clauses
{
    ... //any other logic
}

... //any other tests

/* The key insight, is that Linq will not execute your query until 
you try to access the results. Therefore, you can continue to build
an arbitrarily complex query until you've finished it, and then you
can execute it, as in the below line. */

return results.ToList(); //Execute. Or whatever method you want to use to execute the query.

So by incrementally building our your clauses you have complete flexibility to build arbitrary Linq Where clauses.

Like I said, I disagree with the assertion that progressively building your where clause is bad. Yes, you have to be careful, but it is a technique that gives you great flexibility when your where clause is driven by many different parameters.

Trying to progressively add WHERE clauses is a classic Linq mistake:

  var results = foos.Where(n => n.A == false)
                    .Where(n => n.B == true);

  // Bug: testing C == true, but this ignores A
  results = results.Union(foos.Where(n => n.C == true));

Instead, always use one where clause:

  results = foos.Where(n => n.A == false && (n.B == true || n.C == true));

If the expression gets too large for one line, switch to query syntax:

  results = (from n in foos
             where (n.A == false)
                && (n.B == true || n.C == true)
             select n
            );

As a last resort, if the Where clause gets unreasonably long, use a sql stored procedure instead.

SELECT *
FROM foo
WHERE a = false AND (b = true OR c = true)

would be

foos.Where(x=>!x.a && (x.b || x.c));

alternatively you could do this as well:

foos
  .Where(x=>!x.a)
  .Where(x=>x.b || x.c);

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