2010-08-11 15 views
13

我有一個可變數量的條件,我想將它們放到一個Linq查詢中。在Linq中的循環中添加OR表達式

如何在循環中執行此操作?基本上,最終查詢是成爲:

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 – 2013-03-26 14:47:17

+0

Replacer究竟在幹什麼? – seebiscuit 2014-09-05 20:38:37

+0

@Seabiscuit:它基本上使所得到的表達式樹中的所有參數表達式指向相同的參數表達式,而不是針對頂層和每個「子表達式」分別表示。 – 2014-09-05 20:42:41

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 2010-08-11 06:22:49

+1

是的,你是對的;我錯過了IQueryable位。 – Ani 2010-08-11 12:25:06

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 2018-02-07 11:57:21