C #, Linq2Sql: è possibile concatenare due queryable in uno?

c# expression-trees iqueryable lambda linq-to-sql

Domanda

Ho una queryable dove ho usato varie istruzioni Where and WhereBetween per restringere la raccolta a un certo set. Ora ho bisogno di aggiungere una sorta 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?

Vedo due possibilità:

  1. Crea due queryable da quello che ho, uno che usa Where e uno che usa WhereBetween . E poi concatenarli . Non so se questo è anche possibile? Inoltre, anche se non nel mio caso particolare, molto probabilmente finirai con i duplicati ...
  2. In qualche modo unisci l'espressione Where e l'espressione creata in WhereBetween con una sorta di Or.

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

Il secondo, posso vedere come un'opzione, ma non del tutto sicuro di tutti i dettagli. Di seguito è riportato il metodo WhereBetween della mia altra domanda, che ora uso e funziona alla grande:

    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 potrei forse estrarre l'espressione costruendo parte 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 Expression.OrElse usato nel metodo WhereBetween , e penso che potrebbe essere ciò di cui ho bisogno, o forse questa l' Expression.Or . WhereBetween Ma sono molto instabile con questa espressione, quindi non sono sicuro di cosa scegliere qui, o anche se sono sulla strada giusta: p

Qualcuno potrebbe darmi qualche suggerimento qui?

Risposta accettata

Hai due opzioni qui - Queryable.Union , o combinazione di espressioni. Preferirei generalmente quest'ultimo, via OrElse - che (con LINQ-to-SQL almeno) si può fare con 2 espressioni (vedi sotto) - ma in entrambi i casi dovrebbe essere 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);
}


Autorizzato sotto: CC-BY-SA with attribution
Non affiliato con Stack Overflow
È legale questo KB? Sì, impara il perché
Autorizzato sotto: CC-BY-SA with attribution
Non affiliato con Stack Overflow
È legale questo KB? Sì, impara il perché