linq query with dynamic predicates in where clause joined by OR
Asked Answered
V

2

7

you can easily create dynamic queries in c# if you add more restrictions to the current query.

var list = new List<Item>();
var q = list.AsQueryable();
q = q.Where(x => x.Size == 3);
q = q.Where(x => x.Color == "blue");

In this case, every new predicate is added performing an AND operation with the previous. The previous result is equivalent to:

q = list.Where(x => x.Size == 3 && x.Color == "blue");

Is it possible to achieve the same result but with OR instead of AND?

q = list.Where(x => x.Size == 3 || x.Color == "blue");

The idea is to have a variable number of expressions that are joined with OR operator.

Expected result would need to be written in some how similar to the following pseudo code:

var conditions = new List<Func<Item, bool>>();

And later iterate conditions to build something like:

foreach(var condition in conditions)
    finalExpression += finalExpression || condition;
Ventricle answered 3/9, 2013 at 16:8 Comment(3)
Is there any reason you're using AsQueryable? (It makes a difference in terms of answers, slightly...)Capers
Yes, I ultimately would need to hit the DB with the query, so it should properly be translated by the DB provider. Although, I'm interested in a solution for let's say, an IEnumerableVentricle
take a look at PredicateBuilder : albahari.com/nutshell/predicatebuilder.aspxCristacristabel
V
3

Thanks to Raphaël Althaus that gave the following link:

http://www.albahari.com/nutshell/predicatebuilder.aspx

Predicate builder is the solution. You can use it installing LinqKit from Nuget. In that url you can find also the implementation of this class.

Note: in order to make this work with LinqToSql or LinqToEntities the IQueriable Object must be transformed using "AsExpandable()" method, for memory objects it's not required

Ventricle answered 3/9, 2013 at 17:3 Comment(0)
T
4

Another possible solution to this, especially when someone doesn't want to use an external library is using expression trees.

Add a following expression extension:

public static Expression<Func<T, bool>> Or<T>(
    this Expression<Func<T, bool>> expr1, 
    Expression<Func<T, bool>> expr2)
{
    var invokedExpr = Expression.Invoke(expr2, expr1.Parameters);
    return Expression.Lambda<Func<T, bool>>(
        Expression.OrElse(expr1.Body, invokedExpr), expr1.Parameters);
}

As an example imagine you have a Container entity, which has InnerContainer nested entity with two properties Name and Id. Then you can use it in the following way:

Expression<Func<Container, bool>> whereQuery = c => c.InnerContainer.Name == "Name1";
whereQuery = whereQuery.Or(c => c.InnerContainer.Name == "Name2");
whereQuery = whereQuery.Or(c => c.InnerContainer.Id == 0);
var result = query
    .Where(whereQuery)
    .ToList();

Materializing such query will result in the following SQL:

SELECT [x].[Id], [x].[InnerContainerId]
FROM [Containers] AS [x]
LEFT JOIN [InnerContainer] AS [x.InnerContainer] ON [x].[InnerContainerId] = [x.InnerContainer].[Id]
WHERE [x.InnerContainer].[Name] IN (N'Name1', N'Name2') OR ([x].[InnerContainerId] = 0)

This way you can hold lambdas in a collection and loop through them.

Tinny answered 23/3, 2018 at 20:0 Comment(0)
V
3

Thanks to Raphaël Althaus that gave the following link:

http://www.albahari.com/nutshell/predicatebuilder.aspx

Predicate builder is the solution. You can use it installing LinqKit from Nuget. In that url you can find also the implementation of this class.

Note: in order to make this work with LinqToSql or LinqToEntities the IQueriable Object must be transformed using "AsExpandable()" method, for memory objects it's not required

Ventricle answered 3/9, 2013 at 17:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.