C #, Linq2Sql: Est-il possible de concaténer deux interrogations en une?

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

Question

J'ai un interrogeable où j'ai utilisé diverses instructions Where et WhereBetween pour réduire la collection à un certain ensemble. Maintenant, je dois ajouter une sorte de Where || WhereBetween . En d'autres termes, je ne peux pas les chaîner ensemble comme je l'ai fait jusqu'à présent, car cela fonctionnera comme un Et. Alors, comment puis-je faire cela?

Je vois deux possibilités:

  1. Créez deux objets interrogables à partir de celui que j'ai, l'un en utilisant Where , et l'autre en utilisant WhereBetween . Et ensuite les concaténer . Je ne sais pas si c'est même possible? De plus, bien que ce ne soit pas dans mon cas particulier, vous obtiendrez probablement des doublons ...
  2. D'une certaine manière, fusionner l'expression Where et l'expression créée dans WhereBetween avec une sorte de Ou.

Le premier, comme mentionné, je ne suis pas sûr, est même possible. Et si c'était le cas, je ne suis pas sûr que ce soit un bon moyen de le faire.

La seconde, je peux voir comme une option, mais pas totalement sûre de tous les détails. Ci-dessous, la méthode WhereBetween de mon autre question, que j’utilise maintenant et qui fonctionne très 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));
    }

Je pense que je pourrais peut-être extraire la partie construction de l'expression dans une nouvelle méthode. Peut-être comme ça:

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

Je pourrais ensuite utiliser cette nouvelle méthode pour obtenir l'expression à la place de l'objet interrogeable. Donc, disons que j'ai le WhereBetween(ø => ø.Id, someRange) et par exemple ø => ø.SomeValue == null . Comment puis-je combiner ces deux avec ou? Je regarde l' Expression.OrElse utilisée dans la méthode WhereBetween et je pense que c'est peut-être ce dont j'ai besoin, ou peut-être celle-ci Expression.Or . Mais je suis très instable sur cette expression, donc je ne sais pas trop quoi choisir ici, ni même si je suis sur la bonne voie: p

Quelqu'un pourrait-il me donner des indications ici?

Réponse acceptée

Vous avez deux options ici - Queryable.Union , ou une combinaison d’expression. Je préfère généralement ce dernier, via OrElse - que vous pouvez utiliser avec 2 expressions (avec LINQ-to-SQL au moins) (voir ci-dessous) - mais dans les deux cas, il devrait être composé:

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

Les deux arr1 et arr2 chacun qu'un seul hit de base de données (bien que TSQL soit différent; le premier a un OR dans la clause WHERE ; le second a deux requêtes distinctes avec UNION ).

Voici la méthode d'extension que j'ai utilisée:

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



Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi
Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi