2010-08-11 3 views
13

У меня есть переменное число ИЛИ условия, которые я хочу объединить в один запрос Linq.Добавление выражений OR в цикле в Linq

Как это сделать в цикле? В принципе, окончательный запрос должен быть:

IQueryable<MyObject> Q; 
Q = Q.Where(q => (condition1) || (condition2) || ..... || (condition N)); 

Что-то вроде:

For (int i = 0; i < someNumber; i++) { 
    Q = Q.Where(q => (existing conditions) || (q.Value == i)); 
} 

Какое выражение можно использовать для замены (существующее состояние) в примере выше, не окончательное выражение (Q) вложили Q внутри них?

Спасибо.

ответ

16

Вам нужно будет построить дерево выражений, представляющее все условия, которые вас интересуют, в сочетании с Expression.OrElse, а затем вызовите Where один раз в конце.

Это может быть несколько сложно, если ваш текущий источник является анонимным, но в противном случае это не должно быть слишком плохо. Вот пример: может быть проще использовать замену параметров, но это не так уж плохо. (Хотя ExpressionVisitor работает только в .NET 4 ... Вы должны были бы реализовать что-то подобное себе, если вы хотите использовать это в .NET 3.5.)

using System; 
using System.Linq; 
using System.Linq.Expressions; 

public class Test 
{ 
    static void Main() 
    { 
     IQueryable<string> strings = (new[] { "Jon", "Tom", "Holly", 
      "Robin", "William" }).AsQueryable(); 


     Expression<Func<string, bool>> firstPredicate = p => p.Contains("ll"); 
     Expression<Func<string, bool>> secondPredicate = p => p.Length == 3; 
     Expression combined = Expression.OrElse(firstPredicate.Body, 
               secondPredicate.Body); 

     ParameterExpression param = Expression.Parameter(typeof(string), "p"); 
     ParameterReplacer replacer = new ParameterReplacer(param); 
     combined = replacer.Visit(combined); 

     var lambda = Expression.Lambda<Func<string, bool>>(combined, param); 

     var query = strings.Where(lambda); 

     foreach (string x in query) 
     { 
      Console.WriteLine(x); 
     } 
    } 

    // Helper class to replace all parameters with the specified one 
    class ParameterReplacer : ExpressionVisitor 
    { 
     private readonly ParameterExpression parameter; 

     internal ParameterReplacer(ParameterExpression parameter) 
     { 
      this.parameter = parameter; 
     } 

     protected override Expression VisitParameter 
      (ParameterExpression node) 
     { 
      return parameter; 
     } 
    } 
} 
+0

Только что мне нужно: D –

+0

Что именно делает Replacer? – seebiscuit

+0

@Seabiscuit: в основном, все выражения параметров в результирующем дереве выражений ссылаются на одно и то же выражение параметра, а не на отдельные для верхнего уровня и каждого из «подвыражений». –

2
public static IEnumerable<T> GetItemsThatMatchAny<T> (this IEnumerable<T> source, IEnumerable<Func<T,bool>> predicates) 
    {  
     return source.Where(t => predicates.Any(predicate => predicate(t))); 
    } 

Пример генератора предиката:

private static IEnumerable<Func<MyClass, bool>> GetPredicates (int num) 
{ 
    var predicates = new Func<MyClass, bool>[] {m => m.Foo == 3, m => m.Bar =="x", m => DateTime.Now.DayOfWeek == DayOfWeek.Sunday}; 

    return predicates.Take (num); 
} 
+0

У меня есть аналогичная идея, но, к сожалению, это не с Linq2Sql. – leppie

+1

Да, вы правы; Я пропустил бит IQueryable. – Ani

2

версия, не имеющая оптимизированной версии (молитесь, чтобы бэкэнд выполнил необходимый подъем и оптимизацию).

public static IQueryable<T> Any<T>(this IQueryable<T> q, 
    params Expression<Func<T, bool>>[] preds) 
{ 
    var par = Expression.Parameter(typeof(T), "x"); 

    Expression body = Expression.Constant(false); 

    foreach (var pred in preds) 
    { 
    body = Expression.OrElse(body, Expression.Invoke(pred, par)); 
    } 

    var ff = Expression.Lambda(body, par) as Expression<Func<T, bool>>; 

    return q.Where(ff); 
} 

static void Main(string[] args) 
{ 
    var q = new[] { "jim", "bob", "Jon", "leppie" }.AsQueryable(); 

    Expression<Func<string, bool>>[] preds = 
    { 
    x => x == "Jon", 
    x => x == "Skeet", 
    x => x == "leppie" 
    }; 

    var result = q.Any(preds).ToArray(); 
} 
+0

Не работает для объектов linq ro – moyomeh

Смежные вопросы