Search code examples
c#linqlinq-expressionspredicatebuilderlinq-extensions

Combining two different PredicateBuilders with Expressions


I am having a predicate builder and it is working fine

 var filter = sortKeys.Aggregate(filter, (currentFilter, sortkey) => currentFilter.Or(
                            x => x.Appointments.Any(y => y.RowStatus == Constants.CurrentRowStatus )));

I am now trying to split the conditions which is inside the appointment into another predicate builder so that I can add conditions on the go and reuse the function.

I had tried creating an expression and then using it in the main predicate builder but it is failing

private static Expression<Func<Appointment, bool>> TmpApt(string status)
    {
        var predicate = PredicateBuilder.False<Appointment>();

        predicate = predicate.Or(p => p.RowStatus == status);

        return predicate;
    }

Changed main predicate to use the above expression

var filter = sortKeys.Aggregate(PredicateBuilder.True<Person>(), (current, s) =>
                                current.Or(x => x.Appointments.Any(TmpApt(s))));

It showing an error that

Argument type 'System.Linq.Expressions.Expression<System.Func<Appointment,bool>>' is not assignable to parameter type System.Func<Appointment,bool>

I had even tried LinqKit extension method like Expand but could find a solution.

had also tried Reusable predicate expressions in LINQ, then it is not showing any errors while compiling but when on the application side, it is showing

Unsupported overload used for query operator 'Any'.

Can anyone please help me how to resolve the error or suggest an alternative solution.


Solution

  • You can use LINQKit to invoke the expression that you have at the location that you want to be using it:

    var predicate = TmpApt();
    var filter = sortKeys.Aggregate(PredicateBuilder.False<Person>(),
        (current, s) => current.Or(x =>
            x.Appointments.Any(appointment => predicate.Invoke(appointment))))
            .Expand();
    

    Note that you'll need to pull TmpApt out into a variable for LINQKit to successfully evaluate it, due to a bug in its implementation.

    Also note that you'll want to initialize the aggregate operation to False, because True OR-ed with anything is true.

    Also note that you can simplify the implementation of TmpApt to just the following:

    private static Expression<Func<Appointment, bool>> TmpApt()
    {
        return p => p.RowStatus == Constants.CurrentRowStatus;
    }
    

    There's no need to use a predicate builder to Or it with False here.