2010-08-11 20 views
13

Tengo un número variable de O condiciones que quiero juntar en una consulta Linq.Agregar expresiones O en un bucle en Linq

¿Cómo hago esto en un bucle? Básicamente, la consulta final es ser:

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

Algo así como:

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

Qué comunicado puedo usar para reemplazar (condición existente) en el ejemplo anterior sin tener la expresión final (Q) han anidado Q dentro de ellos?

Gracias.

Respuesta

16

Tendrá que crear un árbol de expresiones que represente todas las condiciones que le interesan, combinado con Expression.OrElse, y luego llamar al Where una sola vez al final.

Esto puede ser un poco complicado si su fuente actual es de tipo anónimo, pero no debería ser tan malo de lo contrario. Aquí hay una muestra: puede haber una manera más simple de hacer la sustitución de parámetros, pero esto no es tan malo. (Aunque ExpressionVisitor sólo funciona en .NET 4 ... habría que aplicar algo similar a sí mismo si quiere utilizar esto en .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

Justo lo que necesitaba: D –

+0

¿Qué está haciendo exactamente el Replacer? – seebiscuit

+0

@Seabiscuit: Básicamente se trata de hacer que todas las expresiones de parámetros en el árbol de expresiones resultante se refieran a la misma expresión de parámetros, en lugar de tener distintas para el nivel superior y cada una de las "subexpresiones". –

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

Un ejemplo de un generador de predicado:

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

Tengo una idea similar, pero lamentablemente esto falla con Linq2Sql. – leppie

+1

Sí, tienes razón; Me perdí el bit IQueryable. – Ani

2

Una versión menos que optimizada (ore que el backend haga la elevación y la optimización necesarias).

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

No funciona para las entidades de rogq ro – moyomeh

Cuestiones relacionadas