Home > front end >  Is there any trick to avoid double checks when constructing Where criteria conditionally?
Is there any trick to avoid double checks when constructing Where criteria conditionally?

Time:07-12

I am wondering whether I can simplify my code below, especially the following 2 statements:

// Statement A
Expression<Func<Product, bool>>? criteria = p =>
    (id == null ? true : p.Id == id) &&
    (name == null ? true : p.Name.ToLower().Contains(name.ToLower())) &&
    (maxPrice == null ? true : p.Price <= maxPrice);


// Statement B
if (id is null && name is null && maxPrice is null)
    criteria = null;

I am not happy with the statement B because of two reasons:

  • I have to check again each parameter that has been checked in the statement A.
  • It is prone to error (forgetting to check all parameters)

Attempt

The following does not compile because

An expression tree cannot contain an assignment operator.

bool sign = false;
Criteria = p =>
    (id == null ? (sign = true) : p.Id == id) &&
    (name == null ? (sign = true) : p.Name.ToLower().Contains(name.ToLower()))
;

if (sign == false)
    Criteria = null;

Question

Is there any way to make it much simpler and less prone to errors? Any suggestions are always welcome.

static IQueryable<Product> Filter(this IQueryable<Product> products,
    int? id = null,
    string? name = null,
    decimal? maxPrice = null)
{
    Expression<Func<Product, bool>>? criteria = p =>
        (id == null ? true : p.Id == id) &&
        (name == null ? true : p.Name.ToLower().Contains(name.ToLower())) &&
        (maxPrice == null ? true : p.Price <= maxPrice);


    if (id is null && name is null && maxPrice is null)
        criteria = null;

    if (criteria is null)
        return products;
    else
        return products.Where(criteria);
}

CodePudding user response:

Just follow the pretty standard pattern of dynamically adding Where based on presence of filtering conditions:

static IQueryable<Product> Filter(this IQueryable<Product> products,
    int? id = null,
    string? name = null,
    decimal? maxPrice = null)
{
    if (id is not null)
    {
        products = products.Where(p => p.Id == id);
    }

    if (name is not null)
    {
        products = products.Where(p => p.Name.ToLower().Contains(name.ToLower()));
    }

    if (maxPrice is not null)
    {
        products = products.Where(p => p.Price <= maxPrice);
    }

    return products;
}

CodePudding user response:

You can check for null on the received parameters, if any are null make that bit of criteria true by checking for null, e.g. (id == null || p.Id== id) basically says give me all Id's that match the parameter id or if it's null then give me all parameter Ids.

static IQueryable<Product> Filter(this IQueryable<Product> products,
    int? id = null,
    string? name = null,
    decimal? maxPrice = null)
{
    Expression<Func<Product, bool>> criteria = p =>
        (id == null || p.Id== id) &&
        (name == null || p.Name.ToLower().Contains(name.ToLower())) &&
        (maxPrice == null || p.Price <= maxPrice);

        return products.Where(criteria);
}

This also has the added benefit of making the parameters optional.

  • Related