Uploaded image for project: 're-motion'
  1. re-motion
  2. RM-3574

Provide a way to register custom expression transformers in the re-linq frontend

    XMLWordPrintable

    Details

      Description

      A new mechanism has been added that allows re-linq-based providers to add custom, light-weight expression transformers to the front-end's query parsing pipeline. This feature can be used to implement simple expression transformers without having to bear the cost of an additional expression visitor run.

      The following classes and interfaces are involved:

      • Interface IExpressionTransformationProvider: Low-level interface for classes providing transformations to be applied by the pipeline. Usually, the ExpressionTransformerRegistry class (see below) is used rather than custom implementations.
      • Class ExpressionTransformerRegistry: A default implementation of IExpressionTransformationProvider which keeps track of transformers implementing the IExpressionTransformer<T> interface. Use the CreateDefault factory method to create an instance of this class that already holds the default transformations defined by re-linq.
      • Interface IExpressionTransformer<T>: Implement this interface to define a custom transformation. The SupportedExpressionTypes property should return the expression node types handled by this transformer, or null if the transformer can handle all node types. T is the static (base) type of the expressions handled by the transformer. The Transform method implements the actual transformation.
      • Class ExpressionTransformationStep: Applies the transformations given by an implementation of IExpressionTransformationProvider to an expression tree. The transformations occur in post-order (transforming child expressions before parent expressions). When a transformation changes an expression, its child expressions and itself will have the matching transformations applied again (and may be transformed again). An instance of this step is included in the default pipeline created by ExpressionTreeParser.CreateDefaultProcessingSteps. The transformations by default run after partial evaluation of the expression tree.

      To include a custom transformation in a LINQ provider, use a QueryParser that includes the transformation.

      The following example creates a highly customizable instance of a class MyQueryable<T>, which has two custom transformers registered. It is of course possible to use a dependency injection container rather than manually composing the queryProvider.

      public static MyQueryable<T> CreateQuery<T>()
      {
        // include default nodes
        var nodeTypeRegistry = MethodCallExpressionNodeTypeRegistry.CreateDefault();
      
        // include default transformations, and add new ones
        var transformerRegistry = ExpressionTransformerRegistry.CreateDefault();
        transformerRegistry.Register (new CustomTransformer1());
        transformerRegistry.Register (new CustomTransformer2());
      
        // use default pipeline
        var processingSteps = ExpressionTreeParser.CreateDefaultProcessingSteps (transformerRegistry);
      
        // create an ordinary QueryParser that uses the objects defined so far
        var expressionTreeParser = new ExpressionTreeParser (nodeTypeRegistry, processingSteps);
        var queryParser = new QueryParser (expressionTreeParser);
      
        // the query executor represents the LINQ provider's back-end
        var queryExecutor = new MyQueryExecutor();
      
        // the query provider plumbs queryParser and queryExecutor together
        var queryProvider = new DefaultQueryProvider (queryParser, queryExecutor);
      
        // the queryable is the entry point in the query - and its execution
        return new MyQueryable<T> (queryProvider);
      }
      

      (Note that QueryParser and DefaultQueryProvider do not change state between different queries, so they can be stored and reused with "singleton" semantics.)

        Attachments

          Issue Links

          There are no Sub-Tasks for this issue.

            Activity

              People

              • Assignee:
                fabian.schmied Fabian Schmied
                Reporter:
                fabian.schmied Fabian Schmied
              • Votes:
                0 Vote for this issue
                Watchers:
                0 Start watching this issue

                Dates

                • Created:
                  Updated:
                  Resolved: