2009-02-20 12 views
11

Ho uno interrogabile dove ho utilizzato varie istruzioni Where e WhereBetween per restringere la raccolta a un determinato set. Ora Devo aggiungere una specie di Where || WhereBetween. In altre parole, non posso semplicemente unirle insieme come ho fatto finora, perché funzionerà come un E. Quindi, come posso fare questo?C#, Linq2Sql: è possibile concatenare due queryable in uno?

Vedo due possibilità:

  1. Creare due queryables da quello che ho, uno con il Where, e uno con WhereBetween. E quindi concatenarli. Non so se questo è anche possibile? Inoltre, anche se non nel mio caso particolare, molto probabilmente finirai con duplicati ...
  2. In qualche modo unire l'espressione Where e l'espressione creata nello WhereBetween con una sorta di Or.

Il primo, come detto, io non sono sicuro è ancora possibile. E se lo fosse, non sono così sicuro che sia un buon modo per farlo.

Il secondo, posso vedere come opzione, ma non completamente sicuro di tutti i dettagli. Di seguito è riportato il metodo WhereBetween dalla mia altra questione, che ora io uso e funziona benissimo:

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

Sto pensando che avrei potuto forse estrarre la porzione di edificio espressione di esso in un nuovo metodo. Forse così:

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

Potrei quindi utilizzare questo nuovo metodo per ottenere l'espressione anziché l'interrogabile. Quindi, diciamo che ho il WhereBetween(ø => ø.Id, someRange) e per esempio ø => ø.SomeValue == null. Come posso combinare questi due con Or? Sto guardando il Expression.OrElse utilizzato nel metodo WhereBetween e penso che potrebbe essere quello che mi serve, o forse questo il Expression.Or. Ma sono molto instabile su questa espressione, quindi non sono sicuro di cosa scegliere qui, o anche se sono sulla buona strada: p

Qualcuno potrebbe darmi qualche suggerimento qui?

risposta

9

Qui ci sono due opzioni: Queryable.Union o combinazione di espressioni. Avevo in genere preferisco quest'ultimo, tramite OrElse - che (con LINQ to SQL, almeno) si può fare con 2 espressioni (vedi sotto) - ma in entrambi i casi dovrebbe avere composto:

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

Entrambi arr1 e arr2 eseguono solo 1 hit di database (sebbene TSQL sia diverso, il primo ha un OR nella clausola WHERE, il secondo ha due query separate con UNION).

Ecco il metodo di estensione che ho usato:

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

Quella cosa fila potrebbe essere niente di buono?(Qualsiasi stringa che sia, ovviamente) – Svish

+0

Pensando alle prestazioni sql e simili, è più efficiente con un 'UNION' o un' OR' nella 'WHERE'clause? – Svish

+0

Molto bello. Questa domanda continua a venire (in variazioni) e il tuo OrElse potrebbe essere solo la pallottola magica che li risolve tutti. –

Problemi correlati