Why are multiple filters applied even if the request is recreated at each iteration

I found this code below in the Filter.cs file in a project created using Microsoft App Studio. Although I am a C # veteran programmer, I have little experience working with LINQ predicate expression developers. I can say that the code below is a “meta-logic” for flexible query construction taking into account the list of filter predicates containing information about the type of field and a set of data values ​​for input into subexpressions. I can not understand how the variable "expression" in the following expression:

query = query.Where(expression).AsQueryable()" 

.. combines the expressions for each field into a more complex query expression, which is finally executed at the end of the code to create an ObservableCollection result. If it were "query + =", I could output a chain action, for example an event handler field, but as a direct assignment operator, it puzzles me, since I expect it to replace the last value obtained from the last iteration of the loop, resetting him in the process and losing previous meanings. What's going on here?

 public class Filter<T> { public static ObservableCollection<T> FilterCollection( FilterSpecification filter, IEnumerable<T> data) { IQueryable<T> query = data.AsQueryable(); foreach (var predicate in filter.Predicates) { Func<T, bool> expression; var predicateAux = predicate; switch (predicate.Operator) { case ColumnOperatorEnum.Contains: expression = x => predicateAux.GetFieldValue(x).ToLower().Contains(predicateAux.Value.ToString().ToLower()); break; case ColumnOperatorEnum.StartsWith: expression = x => predicateAux.GetFieldValue(x).ToLower().StartsWith(predicateAux.Value.ToString().ToLower()); break; case ColumnOperatorEnum.GreaterThan: expression = x => String.Compare(predicateAux.GetFieldValue(x).ToLower(), predicateAux.Value.ToString().ToLower(), StringComparison.Ordinal) > 0; break; case ColumnOperatorEnum.LessThan: expression = x => String.Compare(predicateAux.GetFieldValue(x).ToLower(), predicateAux.Value.ToString().ToLower(), StringComparison.Ordinal) < 0; break; case ColumnOperatorEnum.NotEquals: expression = x => !predicateAux.GetFieldValue(x).Equals(predicateAux.Value.ToString(), StringComparison.InvariantCultureIgnoreCase); break; default: expression = x => predicateAux.GetFieldValue(x).Equals(predicateAux.Value.ToString(), StringComparison.InvariantCultureIgnoreCase); break; } // Why doesn't this assignment wipe out the expression function value from the last loop iteration? query = query.Where(expression).AsQueryable(); } return new ObservableCollection<T>(query); } 
+6
source share
4 answers

I understand that it’s hard for you to understand why this line is executed in a loop

 query = query.Where(expression).AsQueryable(); 

produces an effect similar to the "concatenation" of expressions. Short answer: it looks like why

 str = str + suffix; 

creates a longer string, even if it is a destination.

The longer answer is that the loop builds the expression one predicate at a time and adds Where to the sequence of conditions. Although this is an assignment, it is built from the previous state of the object, so the previous expression is not “lost” because it is used as the basis for a larger and more complex filter expression.

To understand this better, imagine that the individual expressions created by the switch are placed in an array of IQueryable objects instead of appended to query . Once the array of parts is built, you can do this:

 var query = data.AsQueryable() .Where(parts[0]).AsQueryable() .Where(parts[1]).AsQueryable() ... .Where(parts[N]).AsQueryable(); 

Now note that each parts[i] used only once; after that, he is no longer needed. This is why you can consistently build a chain of expressions in a loop: after the first iteration, query contains a chain that includes the first member; after the second iteration, it contains the first two members, etc.

+4
source

It doesn’t “destroy” since the chain . He processes it by assigning a request. It is effective how to write:

 var queryTmp = query; query = queryTmp.Where(expression).AsQueryable(); 

Each time you call .Where(expression).AsQueryable() , a new IQueryable<T> returned and set to query . This IQueryable<T> is the result of the last .Where call. This means that you are effectively getting a request that looks like this:

 query.Where(expression1).AsQueryable().Where(expression2).AsQueryable()... 
+3
source

The code essentially generates a sequence of Where / AsQueryable calls. Not sure why you expect each loop to add expressions.

Essentially the result

 query = query .Where(expression0).AsQueryable() .Where(expression1).AsQueryable() .Where(expression2).AsQueryable() 

where I think you expect more like

 query = query .Where(v => expression0(v) && expression1(v) && expression2(v) ...).AsQueryable() 
+3
source

The variable name query little misleading. This code does not create a long filter in the expression variable, and then runs it against the data set - it runs each filter in the data set one at a time until all filters are running. The query variable contains only all data left over from previously running filters.

So this line:

 query = query.Where(expression).AsQueryable(); 

applies a filter to the existing data stored in the query , and then stores the new (filtered) result back to the variable. The expression value is overwritten every time through the loop, but we no longer need need , because the filter is already applied.

+3
source

All Articles