Как составить «или» предложение в Linq? - PullRequest
5 голосов
/ 19 июня 2011

Если вы добавляете условия "и" в запрос Linq, это легко сделать так:

var q = MyTable;
if (condition1)
  q = q.Where(t => t.Field1 == value1);
if (condition2)
  q = q.Where(t => t.Field2 > t.Field3);
// etc.

Есть ли какой-нибудь умный способ сделать то же самое, когда вы хотите добавитьили условия?

Ответы [ 5 ]

6 голосов
/ 19 июня 2011

Вы можете использовать PredicateBuilder и использовать его для построения выражения Or:

 var predicate = PredicateBuilder.False<Product>();

 predicate = predicate.Or (t => t.Field1 == value1);
 predicate = predicate.Or (t => t.Field2 > t.Field3);

 q = q.Where (predicate);

Подробнее об этом можно прочитать здесь: http://www.albahari.com/nutshell/predicatebuilder.aspx

Замените Product в PredicateBuilder.False<Product>() на ваш запрашиваемый объект.

Обратите внимание, что вы начинаете с предиката False, поскольку хотите использовать Or. Если вы хотите предикат And, Юо следует начать с True

4 голосов
/ 19 июня 2011

Используйте следующее:

var q = MyTable;
q = q.Where(
     t => (condition1 && t.Field1 == value1) || (condition2 && t.Field2 > t.Field3));
2 голосов
/ 19 июня 2011
var q = MyTable;
var conditions = new List<Func<T, bool>>();

 if (condition1)
     conditions.Add(t => ...);
 if (condition2)
     conditions.Add(t => ...);

 q.Where(x => conditions.Any(y => y(x)));
2 голосов
/ 19 июня 2011

Есть один способ сделать это, который включает использование деревьев выражений. Таким образом, вы сами создаете логическое выражение. Сложность в том, что вам нужно перебазировать параметры, потому что в противном случае оно будет ссылаться на исходное лямбда-выражение. Ниже приведен пример:

static void Main(string[] args)
{
    var source = new List<int> { 1, 2, 3 };

    var any = new List<Expression<Func<int, bool>>>();

    any.Add(x => x == 1);
    any.Add(x => x == 3);

    foreach (var item in source.AsQueryable().WhereDisjunction(any))
    {
        Console.WriteLine(item);
    }
}

class RewriteSingleParameterUsage : ExpressionVisitor
{
    public ParameterExpression Parameter { get; set; }

    protected override Expression VisitParameter(ParameterExpression node)
    {
        return Parameter;
    }
}

public static IQueryable<T> WhereDisjunction<T>(this IQueryable<T> source, IList<Expression<Func<T, bool>>> any)
{
    switch (any.Count)
    {
        case 0: return source;
        case 1: return source.Where(any[0]);
        default:
            var p = Expression.Parameter(any[0].Parameters[0].Type, any[0].Parameters[0].Name);
            var rw = new RewriteSingleParameterUsage { Parameter = p };
            var expr = rw.Visit(any[0].Body);
            for (int i = 1; i < any.Count; i++)
            {
                expr = Expression.Or(expr, rw.Visit(any[i].Body));
            }
            return source.Where(Expression.Lambda<Func<T, bool>>(expr, p));
    }
}

В приведенном выше примере я очень резок, я эффективно заменяю любой параметр этим единственным новым параметром, который используется для создания нового выражения. Однако, учитывая сигнатуру этого метода расширения, на самом деле не должно быть возможности вызывать этот метод с параметрами, которые могли бы вызвать ошибку. Однако это будет проблемой, если вы задействуете более одного параметра.

1 голос
/ 19 июня 2011

Это тот же ответ, который я дал здесь

Как сказал Марк Гравелл, это связано с объединением дерева выражений.

В этой статье показано, как это сделать. Требуется немного работы, чтобы первоначально настроить это. Но оно того стоит.

Альтернативным решением является использование Predicate Builder . Статья не очень хорошо объясняет, что на самом деле происходит под капотом. Но вышеприведенная статья это хорошо объясняет

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...