Search code examples
c#entity-frameworklinqentity-framework-6linq-to-entities

Add dynamically predicate to expression without PredicateBuilder


I use linqToEntities and would like to add OR condition dynamically.

I know that there is a great library PredicateBuilder by brother Albahari and it can solve my task, but I cannot use it.

My conditions looks like this:

IEnumerable<Condition> conditions = new List<Condition>()
{
    new Condition(){IdReference = 1, TableName = "Table1" },
    new Condition(){IdReference = 2, TableName = "Table2" },
    new Condition(){IdReference = 3, TableName = "Table3" }, 
    // and so on
};

What I have is:

var histories = db.History as IQueryable<History>;
foreach (var cond in conditions)
{
     //What code should be here to be translated into:
     /*
     histories = histories
        .Where(h => h.IdReference == 1 && h.TableName =="Table1" 
            || h.IdReference == 2 && h.TableName == "Table2"
            || h.IdReference == 3 && h.TableName == "Table3");
            // and so on
     */
}    

However, I do not know in advance how many conditions would be. How is it possible to add OR conditions dynamically from IEnumerable<Condition>?


Solution

  • Not sure what's the problem with using predicate builder - it doesn't have to be LINQ Kit package, the so called predicate builder is usually a single static class with 2 extension methods - like Universal Predicate Builder or my own PredicateUtils from Establish a link between two lists in linq to entities where clause and similar.

    Anyway, once you want it, of course it could be built using just plain Expression class static methods.

    Add the following in order to eliminate the need of writing Expression. before each call:

    using static System.Linq.Expressions.Expression;
    

    and then use something like this:

    if (conditions.Any())
    {
        var parameter = Parameter(typeof(History));
        var body = conditions
            .Select(condition => Expression.Constant(condition))
            .Select(condition => Expression.AndAlso(
            Expression.Equal(
                Expression.PropertyOrField(parameter, nameof(History.IdReference)),
                Expression.Convert(
                      Expression.PropertyOrField(condition, nameof(Condition.IdReference))
                    , Expression.PropertyOrField(parameter, nameof(History.IdReference)).Type)
            ),
            Expression.Equal(
                Expression.PropertyOrField(parameter, nameof(History.TableName)),
                Expression.Convert(
                      Expression.PropertyOrField(condition, nameof(Condition.TableName))
                    , Expression.PropertyOrField(parameter, nameof(History.TableName)).Type)
             )
            ))
            .Aggregate(Expression.OrElse);                      
        var predicate = Lambda<Func<History, bool>>(body, parameter);
        histories = histories.Where(predicate);
    }