Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

linq query with dynamic predicates in where clause joined by OR

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;
like image 782
Pato Loco Avatar asked Sep 03 '13 16:09

Pato Loco


1 Answers

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.

like image 187
Simon Katanski Avatar answered Nov 02 '22 09:11

Simon Katanski