Construction d'un prédicat personnalisé pour agir en tant que filtre à l'aide d'une boucle foreach

c# expression-trees linq-expressions linqkit linq-to-entities

Question

Je dois filtrer une liste de documents en les passant à un filtre personnalisé que j'ai du mal à créer de façon dynamique à l' aide d'une boucle foreach :

var mainPredicate = PredicateBuilder.True<Document>();

// mainPredicate is combined to other filters successfully here ...

var innerPredicate = PredicateBuilder.False<Document>();
foreach (var period in periods)
{
    var p = period;
    Expression<Func<Document, bool>> inPeriod =
        d => d.Date >= p.DateFrom && d.Date <= p.DateTo;

    innerPredicate = innerPredicate.Or(d => inPeriod.Invoke(d));
}

mainPredicate = mainPredicate.And(innerPredicate);

Cette dernière ligne:

documents = this.ObjectSet.AsExpandable().Where(mainPredicate).ToList();

Lance cette exception:

Le paramètre 'd' n'était pas lié dans l'expression de requête LINQ to Entities spécifiée.

Quelqu'un sait pourquoi je reçois cette exception? Je ne comprends pas où le paramètre 'd' que je transmets à la méthode InPeriod est perdu. Je ne sais pas ce qui manque pour que cela fonctionne. Mon code est le même que de nombreux autres exemples qui fonctionnent parfaitement. Toute information théorique supplémentaire sur l'invocation d'expressions et son fonctionnement en coulisse est la bienvenue.

Réponse acceptée

Enfin, j'ai trouvé un moyen d'éviter de combiner plusieurs prédicats avec l'arbre d'expression principal.

Étant donné que chaque prédicat représente un filtre différent et que je souhaite que le filtre final combiné soit une série de conditions à respecter impérativement, nous pouvons dire que chacun des prédicats doit renvoyer vrai pour que le prédicat final redevienne vrai.

Pour que cela fonctionne, les prédicats doivent être combinés avec AND . Ainsi, la requête SQL résultante doit ressembler à ceci:

predicate1 AND predicate2 AND predicate3 ...

Une meilleure façon de combiner ces prédicats avec AND consiste à chaîner les opérateurs de requête Where à la requête finale, comme suit:

var documents = this.ObjectSet.AsExpandable()
    .Where(mainPredicate)
    .Where(otherPredicate)
    .Where(yetAnotherPredicate)
    .ToList();

La requête SQL résultante combinera chacun de ces prédicats avec AND . C'est ce que je voulais faire.

C'est plus facile que de pirater un arbre d'expression seul.


Réponse populaire

Je ne comprends pas pourquoi tu fais ça:

innerPredicate = innerPredicate.Or(d => inPeriod.Invoke(d));

Quand vous pourriez simplement éviter complètement l’ Invoke , comme ceci:

innerPredicate = innerPredicate.Or(inPeriod);

Cela devrait fonctionner parfaitement bien.


BTW, j’ai le sentiment qu’il ya un problème avec LINQKit ici (à moins que certains documents suggèrent qu’il ne supporte pas ce scénario).

Quand j'ai essayé ce code similaire :

 Expression<Func<int, bool>> first = p1 => p1 > 4;
 Expression<Func<int, bool>> second = p2 => p2 < 2;

// Expand is similar to AsExpandable, except it works on 
// expressions, not queryables.
var composite = first.Or(d => second.Invoke(d))
                     .Expand();

... LINQKit a généré l'expression composite suivante:

p1 => ((p1 > 4) OrElse (d < 2)) // what on earth is d?

... qui possède en effet le paramètre non lié d (NodeType = Parameter, Name = 'd').

Esquiver l' Invoke avec first.Or(second).Expand() génère ce qui est parfaitement sensé:

p1 => ((p1 > 4) OrElse (p1 < 2)) // much better now...


Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow