I talked a little bit about patterns using InvocationExpression in a previous post (you might want to review the post for some context). It turns out my advice wasn’t that easy to implement… Fortunately, the EFExtensions helper library now includes an ExpandInvocations method that implements the same functionality (and a little bit more). I’ll return to the same example I used in my previous post, “all cars that are red or cheap”:
Unfortunately, the Entity Framework does not support InvocationExpressions, so the query fails with a NotSupportedException. Our new helper method does the work of rewriting the query to avoid the unsupported invocations:
A lot easier! If you’re interested in the implementation, read on…
Remember math class? Let’s define a couple of functions:
To evaluate g(4), we just expand the values inline:
We just need to teach EFExtensions how to do the same trick. Whenever an invocation is encountered (e.g. “f(y + 2)” in our example), we want to replace the parameter in the body of the function with the corresponding argument. If we’re expanding an expression tree, we just need to remember which argument values correspond to which parameters:
A couple of details of the VisitInvocation method might need explanation. First of all, I’m using the (very handy) Zip method, which allows me to consider the elements of two sequences pair-wise, like the teeth of a zipper coming together. (Good news: Zip is in .NET 4.0!) In this case, the lambda expression’s Parameters are aligned pair-wise with the invocation expression’s Arguments. Second of all, I’m using the call stack to keep track of which parameters are currently in scope. If you squint, InvocationExpander looks a lot like an immutable stack implementation… The _previous field is the ‘tail’ and the _parameter and _expansion fields are the ‘head’. The use of the Aggregate method gives me implicit the Push behavior I’m looking for, adding parameter bindings like layers in an onion. When control returns to the caller of VisitInvocation, the parameters are Popped out of scope, or peeled away, as a side effect of ceding control to the _previous visitor. At every position in the call stack, the InvocationVisitor instance that is in scope also captures the relevant parameter stack. When we run into a parameter expression that potentially requires expansion, we just iterate over the stack until we find a match:
OK… There might be easier ways to write this solution, but I like immutable types in general. Why? I’m not smart enough to figure out state changes. I hate reviewing code and having to ask questions like “have I initialized field x at this point?”, “what happens if someone modifies collection y?” or “do I need to use a lock here?” I’m sure you can find plenty of other good reasons to like them.