This week, we’ve implemented a set of features for easier customizability of the way re-linq’s front-end handles expression trees. With those features, you – as a re-linq-based LINQ provider author – now can:
- extend, customize, or even replace the whole process of how to get from a LINQ expression tree to a re-linq QueryModel;
- add custom expression tree transformation steps (or replace the existing ones); and
- add light-weight expression transformation logic.
I’ll explain those in detail. Consider the following picture, which shows the important classes involved in the execution of a LINQ query with a re-linq based LINQ provider:
As you can see, a user starts the process by either calling GetEnumerator on an instance of a class derived from QueryableBase<T> or by using one of the methods for immediate execution (such as Count, Single, First, etc.) defined by the .NET framework’s Queryable class (passing in an instance derived from QueryableBase<T>). Both cause QueryProviderBase.Execute to be called. Up to here, everything is according to the standard LINQ provider implementation patterns.
Now comes the part specific to re-linq. The QueryProviderBase class will now ask a QueryParser to parse the query into a QueryModel, and then pass that QueryModel to an implementation of IQueryExecutor. The executor must be defined by the actual LINQ provider – this is where the actual data is queried from the underlying data source.
In order to produce the QueryModel, the QueryParser asks an ExpressionTreeParser to parse the given query expression tree into a chain of ExpressionNodeTypes. For each top-level query operator, one (or more) node instances are created, and all of those nodes are then asked to apply themselves onto (and thus building) the QueryModel to be returned.
The ExpressionTreeParser performs several steps before it creates the expression nodes: first, it partially evaluates the expression tree – ie., it simplifies those parts of the tree that can be executed in memory –, then, it applies some transformation steps, and finally, it creates the nodes for the top-level query operators using a user-extensible registry. (For simplicity, I’ve left out the analysis of method call arguments – which involves the detection of sub-queries – from the picture.)
Now that you understand the re-linq (front-end) pipeline, let me try again to explain what we’ve added. You can now:
- extend, customize, or even replace the whole process of how to get from a LINQ expression tree to a re-linq QueryModel by providing a custom query parser;
- add custom expression tree transformation steps (or replace the existing ones) by adding custom expression tree processing steps; and
- add light-weight expression transformation logic by adding custom expression transformers.
I’ll probably describe the details of each of those possibilities in separate blog posts. For now, it will suffice to say that the new customizability features will be released with re-linq build 1.13.92 on CodePlex tomorrow (2011-01-28). By tomorrow evening (CET), the release notes will also be completed (available via JIRA).