Кілька пропозицій 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

до мого попереднього запиту. Як це зробити за допомогою методів розширення?

Дякую!

Відповіді:


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
Muhammad Ashikuzzaman

чи можете ви використовувати це із пропозицією вибору в кінці?

@New_Coder: Звичайно. Речення .Where () не змінює тип повернення.
Девід

це дивно, бо коли я це роблю: Перелічіть <string> paths = db.ClientStatement_Inventory .Where (x => (x.statementYear == yea)) .Where (x => (x.statementMonth == mon)). Виберіть ( c => c.statementPath) .ToList (); Це не працює. але якщо у мене є тільки 1 де, це запитує мою базу даних.

24

Ви можете продовжувати зв’язувати їх, як це робили раніше.

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

Це являє собою І.


Ви - та інші - теж мене биєте, але це, мабуть, найбільш читабельний спосіб це зробити.
Кіт Шредінгер

5
Повторюється, коли до запиту додаються речення з оператором "і" між ними.
linkerro

Це, мабуть, не найчистіше рішення, але в моєму випадку це єдине, що працювало дотепер. Мені довелося додати речення "де" на основі вибору в інтерфейсі.
DJ van Wyk

1
Чи є спосіб зробити це так, що Де є "АБО"?
EK_AllDay

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 () );


Чи є причина, чому це не може працювати з Entity Framework LINQ?
Ciantic

Мені теж добре працює EF EF. Отриманий предикат правильно перекладено в 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: `результати = результати.Де ( бла ).


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)     
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.