简体   繁体   中英

Multiple level where clause filtering with Linq

Say I have some filter criteria passed into my application via an array of CustomerFilter objects where I need to run a query and return the results based on those filters from a Linq to Entities query.

So in this case the customer would be passing me in an array of CustomerFilter objects over the service call.

Filter Object:

class CustomerFilter
{
    public string CustomerID;
    public int[] LocationID;
}

Example Data:

CustomerID    LocationID 

1             1

              2

              3

              4 

2             2 

              3 


              4

I can build a query filtering on the outer CustomerID quite easily as below.

Query:

    public void GetCustomerResults(List<CustomerFilter> accounts)
    {
        List<string> customer = (from a in accounts select a.CustomerID).ToList();

        var locations = ctx.Portal_SurveyLocations
                            .Where(w => customer.Contains(w.CustNum))
                            .OrderBy(o => o.LocationKey);
    }

So I can filter by the outer criteria but I am not sure how to filter by the multiple location IDs for each CustomerID. Obviously just putting an OR clause would give incorrect results as it would pull in other CustomerIDs with matching LocationIDs.

Any ideas of how to accomplish this multiple level filter given the CustomerFilter object passed in?

Slight rework. Basically, we use a combination of Any to traverse the sub-collections to achieve the desired result.

var locations = ctx.Portal_SurveyLocations
    .Where(w => accounts.Any(a => a.CustomerID == w.CustNum &&
                                  a.LocationID.Any(l => w.LocationKey == l)))
    .OrderBy(o => o.LocationKey);

For fast lookup (using Contains on a List isn't very fast) you can create a dictionary of hash sets from the filter objects.

The dictionary would contain one item for each customer, and the value in that item would be a hash set of the locations. When using it, first you check if the customer is in the dictionary, then you check if the location is in the hash set for that customer:

public void GetCustomerResults(List<CustomerFilter> accounts) {
  Dictionary<string, HashSet<int>> lookup =
    accounts.ToDictionary(a => a.CustomerID, a => new HashSet<int>(a.LocationID));

    var locations =
      ctx.Portal_SurveyLocations
      .Where(w =>
        lookup.ContainsKey(w.CustNum) &&
        lookup[w.CustNum].Contains(w.LocationKey))
      .OrderBy(o => o.LocationKey);
}

Both the dictionary and the hash set have an O(1) complexity for looking up an item, so the entire operation gets an O(n+m) complexity, where n is the number of filters, and m is the number of items in Portal_SurveyLocations .

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