Dynamic query with OR conditions in Entity Framework Dynamic query with OR conditions in Entity Framework sql-server sql-server

Dynamic query with OR conditions in Entity Framework


You're probably looking for something like Predicate Builder which allows you to control the AND's and OR's of the where statement easier.

There's also Dynamic Linq which allows you to submit the WHERE clause like a SQL string and it will parse it into the correct predicate for a WHERE.


While LINQKit and its PredicateBuilder are fairly versatile, it's possible to do this more directly with a few simple utilities (each of which can serve as the foundation for other Expression-manipulating operations):

First, a general-purpose Expression Replacer:

public class ExpressionReplacer : ExpressionVisitor{    private readonly Func<Expression, Expression> replacer;    public ExpressionReplacer(Func<Expression, Expression> replacer)    {        this.replacer = replacer;    }    public override Expression Visit(Expression node)    {        return base.Visit(replacer(node));    }}

Next, a simple utility method to replace one parameter's usage with another parameter in a given expression:

public static T ReplaceParameter<T>(T expr, ParameterExpression toReplace, ParameterExpression replacement)    where T : Expression{    var replacer = new ExpressionReplacer(e => e == toReplace ? replacement : e);    return (T)replacer.Visit(expr);}

This is necessary because the lambda parameters in two different expressions are actually different parameters, even when they have the same name. For example, if you want to end up with q => q.first.Contains(first) || q.last.Contains(last), then the q in q.last.Contains(last) must be the exact same q that's provided at the beginning of the lambda expression.

Next we need a general-purpose Join method that's capable of joining Func<T, TReturn>-style Lambda Expressions together with a given Binary Expression generator.

public static Expression<Func<T, TReturn>> Join<T, TReturn>(Func<Expression, Expression, BinaryExpression> joiner, IReadOnlyCollection<Expression<Func<T, TReturn>>> expressions){    if (!expressions.Any())    {        throw new ArgumentException("No expressions were provided");    }    var firstExpression = expressions.First();    var otherExpressions = expressions.Skip(1);    var firstParameter = firstExpression.Parameters.Single();    var otherExpressionsWithParameterReplaced = otherExpressions.Select(e => ReplaceParameter(e.Body, e.Parameters.Single(), firstParameter));    var bodies = new[] { firstExpression.Body }.Concat(otherExpressionsWithParameterReplaced);    var joinedBodies = bodies.Aggregate(joiner);    return Expression.Lambda<Func<T, TReturn>>(joinedBodies, firstParameter);}

We'll use this with Expression.Or, but you could use the same method for a variety of purposes, like combining numeric expressions with Expression.Add.

Finally, putting it all together, you can have something like this:

var searchCriteria = new List<Expression<Func<Name, bool>>();  if (!string.IsNullOrWhiteSpace(first))      searchCriteria.Add(q => q.first.Contains(first));  if (!string.IsNullOrWhiteSpace(last))      searchCriteria.Add(q => q.last.Contains(last));  //.. around 50 additional criteriavar query = Db.Names.AsQueryable();if(searchCriteria.Any()){    var joinedSearchCriteria = Join(Expression.Or, searchCriteria);    query = query.Where(joinedSearchCriteria);}  return query.ToList();


Is there a simple and clean way to add "OR" conditions to a dynamically generated query using entity framework?

Yes, you can achieve this by simply relying on a single where clause containing a single boolean expression whose OR parts are "disabled" or "enabled" dynamically at runtime, thus, avoiding having to install LINQKit or writing a custom predicate builder.

In reference to your example:

var isFirstValid = !string.IsNullOrWhiteSpace(first);var isLastValid = !string.IsNullOrWhiteSpace(last);var query = db.Names  .AsQueryable()  .Where(name =>    (isFirstValid && name.first.Contains(first)) ||    (isLastValid && name.last.Contains(last))  )  .ToList();

As you can see in the example above, we are dynamically switching "on" or "off" the OR-parts of the where-filter expression based on previously evaluated premises (e.g isFirstValid).

For example if isFirstValid is not true, then name.first.Contains(first) is short-circuited and will neither be executed nor affect the resultset. Moreover, EF Core's DefaultQuerySqlGenerator will further optimize and reduce the boolean expression inside where before executing it (e.g. false && x || true && y || false && z may be reduced to simply y through simple static analysis).

Please note: If none of the premises are true, then the result-set will be empty – which I assume is the desired behavior in your case. However, if you for some reason rather prefer to select all elements from your IQueryable source, then you may add a final variable to the expression evaluating to true (e.g. .Where( ... || shouldReturnAll) with var shouldReturnAll = !(isFirstValid || isLastValid) or something similar).

A final remark: The downside of this technique is that it forces you to build a "centralized" boolean expression that resides in the same method body in which your query lies (more precisely the where part of the query). If you, for some reason, want to decentralize the build process of your predicates and inject them as arguments or chain them via the query builder, then you should better stick with a predicate builder as suggested in the other answers. Otherwise, enjoy this simple technique :)