简体   繁体   中英

Linq where clause “if item is not null, evaluate, otherwise, get all data”

I'm trying to implement a filter form on a web client which could have any number of items either filled out or empty. My WebAPI method will take a model that is all of the properties of the form, it would be like:

RequisitionType (enum: RequisitionType.RequisitionedBy, RequisitionType.CreatedBy)
Vendor (string or null)
Plant (string or null)
CompanyCode (string or null)
CreatedFrom (datetime)
CreatedTo (datetime)

How could I implement a linq query without have many crazy if (condition) { filter } ?

I've tried something like this, where I first check if the filter has a value and if it does I will compare the field to that value, otherwise I'll compare the field to itself, but the compiler isn't allowing that.

In the example below, model is what's passed to the controller from the client.

var data =  logic.GetPurchaseReqs()
            .Where(pr => {
                model.RequisitionType == RequisitionType.RequisitionedBy ? pr.PrReqId.ToUpper() == model.Username.ToUpper() : pr.PrReqId.ToUpper() == pr.PrReqId.ToUpper()
                && model.RequisitionType == RequisitionType.CreatedBy ? pr.PrCreId.ToUpper() == model.Username.ToUpper() : pr.PrCreId.ToUpper() == pr.PrCreId.ToUpper()
                && model.Vendor != null ? pr.Vendor == model.Vendor : pr.Vendor == pr.Vendor
                [etc...]
            })
            .ToList();

I believe the easiest way to simplify this would be:

logic.GetPurchaseReqs()
     .Where(pr => model.RequisitionType != RequisitionType.RequisitionedBy || pr.PrReqId.ToUpper() == model.Username.ToUpper())
     .Where(pr => model.RequisitionType != RequisitionType.CreatedBy || pr.PrCreId.ToUpper() == model.Username.ToUpper())
     .Where(pr => model.Vendor == null || pr.Vendor == model.Vendor)
     //[...]
     .ToList();

Note that adding several Where clauses introduces miniscule overhead (just the processing power required for the JIT to call a function), but IMHO improves readability.

For readability and since it is almost the same amount of code I would definitely go with the if(condition) solution.

var data = logic.GetPurchaseReqs(); //.AsEnumerable() migt be needed here

if(model.RequisitionType == RequisitionType.RequisitionedBy)
  data = data.Where(pr => pr.PrReqId.ToUpper() == model.Username.ToUpper())

if(model.Vendor != null)
  data = data.Where(pr => pr.Vendor == model.Vendor);

if(model.CompanyCode != null)
  data = data.Where(pr => pr.CompanyCode == model.CompanyCode);

...

Not completely following what you are trying to do but based on your title:

var data = logic.GetPurchaseReqs();

if(condition)
{
    data = data.Where(pr => pr.... etc
}

var result = data.ToList();

This is what I would program it as. Note the lack of brackets and the way I handled the requisition type. This assumes that requisition type cannot be null...if it CAN...then you have to add a 3rd condition || model.RequisitionType == null || model.RequisitionType == null . Otherwise, each condition you check gets added on as another && (condition != null ? conditionCheck : true)

var data = logic.GetPruchaseReqs().Where(pr =>
    ((model.RequisitionType == RequisitionType.RequisitionedBy && pr.PrReqId.Equals(model.Username, StringComparison.InvariantCultureIgnoreCase)) || (model.RequisitionType == RequisitionType.CreatedBy && pr.PrCreId.Equals(model.Username, StringComparison.InvariantCultureIgnoreCase))) 
    && (model.Vendor != null ? pr.Vendor.Equals(model.Vendor) : true) 
    && (condition != null ? conditionCheck : true)).ToList();

The goal is to setup the right side of the => to evaluate down to a single boolean true/false. You should be able to evaluate the statement for any given value of (pr) and get either true or false.

The best solution would be to create yourself an extension method for linq. It will take in a conditional statment and a where expression, and if the condition is true it will use the where expression, else it will skip it. This can dramatically clean up your code, eliminating the need for if statements.

public static class LinqExtension
{
    public static IQueryable<T> Where<T>(this IQueryable<T> query, bool condition, Expression<Func<T, bool>> whereClause)
    {
        if (condition)
        {
            return query.Where(whereClause);
        }
        return query;
    }
}

Now you can do conditional where statements without using if statements.

var data = logic.GetPurchaseReqs()
                .Where(model.RequisitionType == RequisitionType.RequisitionedBy, pr => pr.PrReqId.ToUpper() == model.Username.ToUpper())
                .Where(model.Vendor != null, pr => pr.Vendor == model.Vendor)
                .Where(model.CompanyCode != null, pr => pr.CompanyCode == model.CompanyCode);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM