2009-02-20 19 views
11

Tengo un consultable donde he usado varias declaraciones Where y WhereBetween para reducir la colección a un cierto conjunto. Ahora Necesito agregar un tipo de Where || WhereBetween. En otras palabras, no puedo encadenarlos como lo hice hasta ahora, porque eso funcionará como un Y. Entonces, ¿cómo puedo hacer esto?C#, Linq2Sql: ¿Es posible concatenar dos consultas en una?

veo dos posibilidades:

  1. Cree dos queryables de la que tengo, uno usando el Where, y el otro con WhereBetween. Y luego concatenarlos. No sé si esto es posible? También, aunque no en mi caso particular, lo más probable es acabar con los duplicados ...
  2. De alguna manera se funden la expresión Where y la expresión creada en el WhereBetween con algún tipo de O.

La primera, como se ha mencionado, no estoy seguro es incluso posible. Y si lo fue, no estoy tan seguro de que sea una buena forma de hacerlo.

El segundo, puedo verlo como una opción, pero no estoy totalmente seguro de todos los detalles. A continuación se muestra el método de WhereBetween de mi otra pregunta, que ahora utilizo y funciona muy bien:

public static IQueryable<TSource> WhereBetween<TSource, TValue>(
     this IQueryable<TSource> source, 
     Expression<Func<TSource, TValue>> selector, 
     IEnumerable<Range<TValue>> ranges) 
    { 
     var param = Expression.Parameter(typeof(TSource), "x"); 
     var member = Expression.Invoke(selector, param); 
     Expression body = null; 
     foreach (var range in ranges) 
     { 
      var filter = Expression.AndAlso(
       Expression.GreaterThanOrEqual(member, 
        Expression.Constant(range.A, typeof(TValue))), 
       Expression.LessThanOrEqual(member, 
        Expression.Constant(range.B, typeof(TValue)))); 
      body = body == null ? filter : Expression.OrElse(body, filter); 
     } 
     return body == null ? source : source.Where(
      Expression.Lambda<Func<TSource, bool>>(body, param)); 
    } 

estoy pensando que tal vez podría extraer la parte de la construcción de expresión en un nuevo método. Tal como esto:

public static IQueryable<TSource> WhereBetween<TSource, TValue>(
     this IQueryable<TSource> source, 
     Expression<Func<TSource, TValue>> selector, 
     IEnumerable<Range<TValue>> ranges) 
    { 
     return source.Where(WhereBetween(selector, ranges)); 
    } 

    public static Expression<Func<TSource, bool>> WhereBetween<TSource, TValue>(
     Expression<Func<TSource, TValue>> selector, 
     IEnumerable<Range<TValue>> ranges) 
    { 
     var param = Expression.Parameter(typeof(TSource), "x"); 
     var member = Expression.Invoke(selector, param); 
     Expression body = null; 
     foreach (var range in ranges) 
     { 
      var filter = Expression.AndAlso(
       Expression.GreaterThanOrEqual(member, 
        Expression.Constant(range.A, typeof(TValue))), 
       Expression.LessThanOrEqual(member, 
        Expression.Constant(range.B, typeof(TValue)))); 
      body = body == null ? filter : Expression.OrElse(body, filter); 
     } 
     return body == null 
      ? ø => true 
      : Expression.Lambda<Func<TSource, bool>>(body, param); 
    } 

que podrían entonces utilizar ese nuevo método para obtener la expresión en lugar de la consultable. Entonces, digamos que tengo el WhereBetween(ø => ø.Id, someRange) y por ejemplo ø => ø.SomeValue == null. ¿Cómo puedo combinar esos dos con O? Estoy mirando el Expression.OrElse utilizado en el método WhereBetween, y creo que podría ser lo que necesito, o tal vez este el Expression.Or. Pero soy muy inestable en esta expresión, así que no estoy seguro de qué elegir aquí, o incluso si estoy en el camino correcto: p

¿Podría alguien darme algunos consejos aquí?

Respuesta

9

Tiene dos opciones aquí - Queryable.Union, o combinación de expresiones. Me había por lo general a favor de este último, a través de OrElse - la cual (con LINQ a SQL al menos) se puede hacer con 2 expresiones (véase más adelante) - pero en cualquier caso se debe conseguir compuesta:

using(var ctx = new DataClasses1DataContext()) 
    { 
     ctx.Log = Console.Out; 
     Expression<Func<Customer, bool>> lhs = 
      x => x.Country == "UK"; 
     Expression<Func<Customer, bool>> rhs = 
      x => x.ContactName.StartsWith("A"); 

     var arr1 = ctx.Customers.Where(
      lhs.OrElse(rhs)).ToArray(); 

     var arr2 = ctx.Customers.Where(lhs) 
      .Union(ctx.Customers.Where(rhs)).ToArray(); 
    } 

Tanto arr1 y arr2 cada uno solo realizan 1 hit de base de datos (aunque el TSQL es diferente; el primero tiene OR en la cláusula WHERE; el segundo tiene dos consultas separadas con UNION).

Aquí está el método de extensión utilicé:

static Expression<Func<T, bool>> OrElse<T>(
    this Expression<Func<T, bool>> lhs, 
    Expression<Func<T, bool>> rhs) 
{ 
    var row = Expression.Parameter(typeof(T), "row"); 
    var body = Expression.OrElse(
     Expression.Invoke(lhs, row), 
     Expression.Invoke(rhs, row)); 
    return Expression.Lambda<Func<T, bool>>(body, row); 
} 
+0

Esa cosa fila podría ser cualquier cosa, ¿verdad?(Cualquier cadena que sea, por supuesto) – Svish

+0

Pensando en el rendimiento sql y tal, ¿es más eficiente con un 'UNION' o un' OR' en la cláusula 'WHERE'? – Svish

+0

Muy agradable. Esta pregunta sigue apareciendo (en variaciones) y tu OrElse podría ser la bala mágica que los resuelve a todos. –

Cuestiones relacionadas