Несколько предложений WHERE с методами расширения LINQ

79

У меня есть запрос LINQ, который выглядит следующим образом:

DateTime today = DateTime.UtcNow;
var results = from order in context.Orders
              where ((order.OrderDate <= today) && (today <= order.OrderDate))
              select order;

Я пытаюсь изучить / понять LINQ. В некоторых случаях мне нужно добавить два дополнительных предложения WHERE. Чтобы сделать это, я использую:

if (useAdditionalClauses)
{
  results = results.Where(o => o.OrderStatus == OrderStatus.Open)  // Now I'm stuck.
}

Как видите, я знаю, как добавить дополнительное предложение WHERE. Но как мне добавить несколько? Например, я бы хотел добавить

WHERE o.OrderStatus == OrderStatus.Open AND o.CustomerID == customerID

на мой предыдущий запрос. Как это сделать с помощью методов расширения?

Спасибо!

user609886
источник

Ответы:

151

Два пути:

results = results.Where(o => (o.OrderStatus == OrderStatus.Open) &&
                             (o.CustomerID == customerID));

или же:

results = results.Where(o => (o.OrderStatus == OrderStatus.Open))
                 .Where(o => (o.CustomerID == customerID));

Я обычно предпочитаю последнее. Но стоит профилировать SQL-сервер, чтобы проверить выполнение запроса и посмотреть, какой из них лучше работает с вашими данными (если вообще есть разница).

Замечание об объединении .Where()методов: вы можете объединить в цепочку все методы LINQ, которые хотите. Такие методы, как .Where()фактически, не выполняются в базе данных (пока). Они откладывают выполнение до тех пор, пока не будут вычислены фактические результаты (например, с помощью a .Count()или a .ToList()). Итак, когда вы объединяете несколько методов (больше вызовов .Where(), может быть, .OrderBy()или чего-то подобного и т. Д.), Они создают то, что называется деревом выражений . Все это дерево - это то, что выполняется в отношении источника данных, когда приходит время его оценить.

Дэвид
источник
2
Я чувствую себя глупо, не зная, что смогу это сделать ... Вы только что спасли меня от такого количества спагетти-кода.
ledgeJumper
Спасибо, это мне помогло. Но возможно ли также, что я вызову любое из предложений where в зависимости от определенной переменной? @David
Мухаммад Ашикуззаман
вы можете использовать это с предложением select в конце?
@New_Coder: Конечно. Предложение .Where () не меняет тип возвращаемого значения.
Дэвид
это странно, потому что когда я делаю это: List <string> paths = db.ClientStatement_Inventory .Where (x => (x.statementYear == yea)) .Where (x => (x.statementMonth == mon)) .Select ( c => c.statementPath) .ToList (); Не работает. но если у меня есть только одно предложение where, оно запрашивает мою базу данных.
24

Вы можете продолжить их цепочку, как вы это сделали.

results = results.Where (o => o.OrderStatus == OrderStatus.Open);
results = results.Where (o => o.InvoicePaid);

Это представляет собой И.

Брайан Ботчер
источник
Вы - и другие - тоже победили меня, но это, вероятно, самый читаемый способ сделать это.
Schroedingers Cat
5
Повторяется, где предложения добавляются к запросу с оператором «и» между ними.
linkerro 09
Вероятно, это не самое «чистое» решение, но в моем случае это единственное, что работало до сих пор. Мне пришлось добавить предложения where на основе выбора в пользовательском интерфейсе.
DJ van Wyk,
1
Есть ли способ сделать это так, чтобы было написано «ИЛИ»?
EK_AllDay 07
11

Если вы работаете с данными в памяти (читайте «коллекции POCO»), вы также можете складывать свои выражения вместе с помощью PredicateBuilder следующим образом:

// initial "false" condition just to start "OR" clause with
var predicate = PredicateBuilder.False<YourDataClass>();

if (condition1)
{
    predicate = predicate.Or(d => d.SomeStringProperty == "Tom");
}

if (condition2)
{
    predicate = predicate.Or(d => d.SomeStringProperty == "Alex");
}

if (condition3)
{
    predicate = predicate.And(d => d.SomeIntProperty >= 4);
}

return originalCollection.Where<YourDataClass>(predicate.Compile());

Полный источник упомянутого PredicateBuilderниже (но вы также можете проверить исходную страницу с еще несколькими примерами):

using System;
using System.Linq;
using System.Linq.Expressions;
using System.Collections.Generic;

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

  public static Expression<Func<T, bool>> Or<T> (this Expression<Func<T, bool>> expr1,
                                                      Expression<Func<T, bool>> expr2)
  {
    var invokedExpr = Expression.Invoke (expr2, expr1.Parameters.Cast<Expression> ());
    return Expression.Lambda<Func<T, bool>>
          (Expression.OrElse (expr1.Body, invokedExpr), expr1.Parameters);
  }

  public static Expression<Func<T, bool>> And<T> (this Expression<Func<T, bool>> expr1,
                                                       Expression<Func<T, bool>> expr2)
  {
    var invokedExpr = Expression.Invoke (expr2, expr1.Parameters.Cast<Expression> ());
    return Expression.Lambda<Func<T, bool>>
          (Expression.AndAlso (expr1.Body, invokedExpr), expr1.Parameters);
  }
}

Примечание . Я тестировал этот подход с помощью проекта Portable Class Library и должен использовать, .Compile()чтобы он работал:

Где (предикат .Compile () );

Sevenate
источник
Есть ли причина, по которой это не работает с Entity Framework LINQ?
Ciantic
У меня тоже отлично работает с EF Core. Результирующий предикат правильно транслируется в SQL.
Томас Гильберт
5

Конечно:

if (useAdditionalClauses) 
{ 
  results = 
    results.Where(o => o.OrderStatus == OrderStatus.Open && 
    o.CustomerID == customerID)  
} 

Или просто еще один .Where()вызов, подобный этому (хотя я не знаю, зачем вам это нужно, если он не разделен другой логической управляющей переменной):

if (useAdditionalClauses) 
{ 
  results = results.Where(o => o.OrderStatus == OrderStatus.Open).
    Where(o => o.CustomerID == customerID);
} 

Или другое переназначение на results: `results = results.Where ( бла ).

Андраш Золтан
источник
2

вы можете использовать && и записать все условия в одно и то же предложение where или вы можете .Where (). Where (). Where () ... и так далее.

Джош С.
источник
1
results = context.Orders.Where(o => o.OrderDate <= today && today <= o.OrderDate)

Выбор не требуется, так как вы уже работаете с заказом.

Гент
источник
0

Просто используйте &&оператор, как и любой другой оператор, который вам нужен для логической логики.

if (useAdditionalClauses)
{
  results = results.Where(
                  o => o.OrderStatus == OrderStatus.Open 
                  && o.CustomerID == customerID)     
}
cadrell0
источник