Construire une expression de requête OR progressivement

Dans LINQ, on peut construire une requête LINQ progressivement comme suit:

var context = new AdventureWorksDataContext(); // Step 1 var query = context.Customers.Where(d => d.CustomerType == "Individual"); // Step 2 query = query.Where(d => d.TerritoryID == 3); 

La requête ci-dessus produirait une instruction SQL équivalente avec une clause WHERE comprenant deux prédicats combinés par un opérateur logique AND , comme suit:

 SELECT * FROM Customers WHERE CustomerType = 'Individual' AND TerritoryID = 3 

Peut-on créer une requête LINQ pour générer progressively une instruction SQL équivalente, de sorte que la requête résultante comporte une clause WHERE avec les prédicats combinés par un opérateur logique OR , comme suit?

 SELECT * FROM Customers WHERE CustomerType = 'Individual' OR TerritoryID = 3 

Vous devez d’abord construire les filtres, puis les combiner en un seul lambda que vous pouvez utiliser en tant que requête composée:

 var filters = new List>>(); filters.Add(d => d.TerritoryID == 3); filters.Add(d => d.CustomerType == "Individual"); ... var lambda = AnyOf(filters.ToArray()); // this is: d => d.TerrotoryID == 3 || d.CustomerType == "Individual"; var data = src.Where(lambda); 

En utilisant:

 static Expression> AnyOf( params Expression>[] expressions) { if (expressions == null || expressions.Length == 0) return x => false; if (expressions.Length == 1) return expressions[0]; var body = expressions[0].Body; var param = expressions[0].Parameters.Single(); for (int i = 1; i < expressions.Length; i++) { var expr = expressions[i]; var swappedParam = new SwapVisitor(expr.Parameters.Single(), param) .Visit(expr.Body); body = Expression.OrElse(body, swappedParam); } return Expression.Lambda>(body, param); } class SwapVisitor : ExpressionVisitor { private readonly Expression from, to; public SwapVisitor(Expression from, Expression to){ this.from = from; this.to = to; } public override Expression Visit(Expression node) { return node == from ? to : base.Visit(node); } } 

si vous le souhaitez en deux étapes, vous pouvez utiliser l’union:

 var context = new AdventureWorksDataContext(); // Step 1 var query = context.Customers.Where(d => d.CustomerType == "Individual"); // step2 query = query.Union(context.Customers.Where(d => d.TerritoryID == 3)); 

Pour accomplir ce que vous demandez sans utiliser la bibliothèque dynamic linq lib, vous pouvez définir des expressions pour chaque test dans la clause where, puis les utiliser pour créer une expression lambda:

 Expression> isIndividualCustomer = c => c.CustomerType == "Individual"; Expression> territoryIdIs3 = c => c.TerritoryID == 3; Expression> customerIsIndividualOrOfTerritoryId3 = Expression.Lambda>( Expression.Or(isIndividualCustomer.Body, territoryIdIs3.Body), isIndividualCustomer.Parameters.Single()); 

Usage:

 var query = context.Customers.Where(customerIsIndividualOrOfTerritoryId3); 

Bien que vous puissiez faire ce genre de chose directement, j’utilise la bibliothèque LinqKit qui facilite vraiment la construction progressive de requêtes:

Vous vous retrouveriez avec:

 var context = new AdventureWorksDataContext(); // Step 1 var query = context.Customers.Where(d => d.CustomerType == "Individual"); // Step 2 query = query.Or(d => d.TerritoryID == 3); 

Le système sur lequel je travaille nécessite beaucoup de cela, et LinqKit est un épargnant majeur. Vous pouvez le trouver ici .

(Remarque: de toute façon, je ne suis pas affilié au développeur de LinqKit – juste un fan.)