Search code examples
predicateslinqkit

LinqKit Predicates


I am trying to figure out how the Predicates work. I have a piece of code where one parameter will always be supplied but there could be up to 5 different parameters.

If I try this way

var predicate = PredicateBuilder.False<Data.AccountAllocation>();                  

if (startDate.HasValue)
   predicate = predicate.And(p => p.DateEntered >= startDate);

if (endDate.HasValue)
   predicate = predicate.And(p => p.DateEntered <= endDate);

if (allocationTypeId.HasValue)
    predicate = predicate.And(p => p.AllocationTypeID == allocationTypeId);

if (allocationStatusID.HasValue)
    predicate = predicate.And(p => p.AllocationStatusTypeID == allocationStatusID);

var accountAllocation = await db.AccountAllocations.AsExpandable().Where(predicate).ToListAsync();

return accountAllocation;

It returns nothing yet if I write it this way

var predicate = PredicateBuilder.False<Data.AccountAllocation>();

if (accountId > 0)
   predicate = predicate.Or(p => p.AccountID == accountId);

if (startDate.HasValue)
   predicate = predicate.And(p => p.DateEntered >= startDate);

if (endDate.HasValue)
   predicate = predicate.And(p => p.DateEntered <= endDate);

if (allocationTypeId.HasValue)
   predicate = predicate.And(p => p.AllocationTypeID == allocationTypeId);

if (allocationStatusID.HasValue)
   predicate = predicate.And(p => p.AllocationStatusTypeID == allocationStatusID);

var accountAllocation = await db.AccountAllocations.AsExpandable().Where(predicate).ToListAsync();

return accountAllocation;

It works properly. If I change the first Predicate, the account, from .Or to .And it does not work.

.Or always seems to run but if I put .Or for all of them the date returned is not correct since it needs to be an .And

I am trying to figure out how to get this to work because there will be a time where all the parameters are optional. and I will not be able to use a .Or, what is the secret to getting the .And to work regardless oh how many of the parameters get added.


Solution

  • If you only evaluate And conditions you must start with a True predicate, basically because false && bool1 && bool2 ... always evaluates to false:

    var predicate = PredicateBuilder.True<Data.AccountAllocation>();
    

    But when there is an Or predicate in the chain of predicates the expression becomes true if the Or predicate evaluates to true.

    You probably start with a False predicate because you don't want to return any data of not a single parameter is entered. You can achieve this by checking the predicate at the end:

    var predicate = PredicateBuilder.True<Data.AccountAllocation>();
    var initString = predicate.ToString();
    
    if (startDate.HasValue)
       predicate = predicate.And(p => p.DateEntered >= startDate);
    
    ...
    
    if (predicate.ToString() == initString)
        predicate = predicate.And(p => false);