A Where
clause does not create a new object when invoked; it applies a filter to an existing collection by wrapping the collection in a filter that will eventually be run. However, since it is a pure function, the return value of that filter needs to be returned to the original reference myQuery
, which is why the second example works...you have chained the result through the Where()
clause back to myQuery
. You return the filtered collection back, materializing the deferred query with ToList()
.
In the first example, you are assuming that the filter is applied directly to the collection, but that isn't how LINQ
works, since the filter doesn't modify the collection immediately when the function is called. Rather, it only applies a predicate that WILL be applied provided it is "attached" to the original Queryable
collection and you resolve the query with ToList()
.
It is sometimes easier to think about deferred execution as a series of promises.
If I give you a dollar, you have a dollar. If I take 25 cents for taxes, I've immediately resolved our transaction (query).
However, if I promise to give you a dollar on Tuesday, I've returned a Queryable<T>
promise.
Multiple events could occur however between now and Tuesday. I could chain a filter for taxes (25 cents), a filter for a stick of gum (25 cents), or any other filter.
However, our bookkeeping system has a caveat. We can't just call .Taxes()
(our Where
clause) on the total promised amount and expect it to update. We have to record our transaction against the promised amount by returning the filter to the original variable, updating it with the transaction that has occurred against our promised amount.
myQuery = myQuery.Where(condition);
On Tuesday, when you come to collect your payment (by calling ToList()
on the series of chained promises/filters), I deduct what has occurred from what was promised, resulting in a 50 cent payout. That's deferred execution at work.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…