/// We compile each entityQuery with EqlCompiler and build a Select call from the fields
            public override DataApiNode VisitEntityQuery(EqlGrammerParser.EntityQueryContext context)
            {
                string name;
                string query;

                if (context.alias != null)
                {
                    name  = context.alias.name.GetText();
                    query = context.entity.GetText();
                }
                else
                {
                    query = context.entity.GetText();
                    name  = query;
                    if (name.IndexOf(".") > -1)
                    {
                        name = name.Substring(0, name.IndexOf("."));
                    }
                }

                try
                {
                    if (_selectContext == null)
                    {
                        // top level are queries on the context
                        var exp            = EqlCompiler.Compile(query, _schemaProvider, _methodProvider).Expression;
                        var topLevelSelect = BuildDynamicSelectOnCollection(exp, name, context, true);
                        return(topLevelSelect);
                    }
                    // other levels are object selection. e.g. from the top level people query I am selecting all their children { field1, etc. }
                    return(BuildDynamicSelectForObjectGraph(query, name, context));
                }
                catch (EqlCompilerException ex)
                {
                    //return DataApiNode.MakeError(name, $"Error compiling field or query '{query}'. {ex.Message}");
                    throw DataApiException.MakeFieldCompileError(query, ex.Message);
                }
            }
Beispiel #2
0
 /// <summary>
 /// Visit a parse tree produced by <see cref="EqlGrammerParser.entityQuery"/>.
 /// <para>
 /// The default implementation returns the result of calling <see cref="AbstractParseTreeVisitor{Result}.VisitChildren(IRuleNode)"/>
 /// on <paramref name="context"/>.
 /// </para>
 /// </summary>
 /// <param name="context">The parse tree.</param>
 /// <return>The visitor result.</return>
 public virtual Result VisitEntityQuery([NotNull] EqlGrammerParser.EntityQueryContext context)
 {
     return(VisitChildren(context));
 }
            /// Given a syntax of someField { fields, to, selection, from, object }
            /// it will figure out if 'someField' is an IEnumerable or an istance of the object (not a collection) and build the correct select statement
            private DataApiNode BuildDynamicSelectForObjectGraph(string query, string name, EqlGrammerParser.EntityQueryContext context)
            {
                if (!_schemaProvider.TypeHasField(_selectContext.Type.Name, name))
                {
                    throw new EqlCompilerException($"Type {_selectContext.Type} does not have field or property {name}");
                }
                name = _schemaProvider.GetActualFieldName(_selectContext.Type.Name, name);

                // Don't really like any of this, but...
                try
                {
                    var result = EqlCompiler.CompileWith(query, _selectContext, _schemaProvider, _methodProvider);
                    var exp    = result.Expression;
                    if (exp.Body.Type.IsEnumerable())
                    {
                        return(BuildDynamicSelectOnCollection(exp, name, context, false));
                    }

                    var oldContext = _selectContext;
                    _selectContext = exp.Body;
                    // visit child fields. Will be field or entityQueries again
                    var fieldExpressions = context.fields.children.Select(c => Visit(c)).Where(n => n != null).ToList();

                    var relationsExps = fieldExpressions.Where(f => f.Expression.NodeType == ExpressionType.MemberInit || f.Expression.NodeType == ExpressionType.Call).ToList();
                    if (_relationHandler != null && relationsExps.Any())
                    {
                        var parameterExpression = Expression.Parameter(_selectContext.Type);
                        var relations           = relationsExps.Select(r => (Expression)Expression.PropertyOrField(parameterExpression, r.Name)).ToList();
                        exp = _relationHandler.BuildNodeForSelect(relations, parameterExpression, exp, name, _schemaProvider);
                    }

                    var newExp = DataApiExpressionUtil.CreateNewExpression(_selectContext, fieldExpressions, _schemaProvider);
                    _selectContext = oldContext;
                    return(new DataApiNode(_schemaProvider.GetActualFieldName(_selectContext.Type.Name, name), newExp, exp.Parameters.Any() ? exp.Parameters.First() : null, exp.Body));
                }
                catch (EqlCompilerException ex)
                {
                    throw DataApiException.MakeFieldCompileError(query, ex.Message);
                }
            }
            /// Given a syntax of someCollection { fields, to, selection, from, object }
            /// it will build a select assuming 'someCollection' is an IEnumerable
            private DataApiNode BuildDynamicSelectOnCollection(LambdaExpression exp, string name, EqlGrammerParser.EntityQueryContext context, bool isRootSelect)
            {
                var elementType      = exp.Body.Type.GetEnumerableType();
                var contextParameter = Expression.Parameter(elementType);

                var oldContext = _selectContext;

                _selectContext = contextParameter;
                // visit child fields. Will be field or entityQueries again
                var fieldExpressions = context.fields.children.Select(c => Visit(c)).Where(n => n != null).ToList();
                var relations        = fieldExpressions.Where(f => f.Expression.NodeType == ExpressionType.MemberInit || f.Expression.NodeType == ExpressionType.Call).Select(r => r.RelationExpression).ToList();

                // process at each relation
                if (_relationHandler != null && relations.Any())
                {
                    // Likely the EF handler to build .Include()s
                    exp = _relationHandler.BuildNodeForSelect(relations, contextParameter, exp, name, _schemaProvider);
                }

                // we're about to add the .Select() call. May need to do something
                if (_relationHandler != null && isRootSelect)
                {
                    exp = _relationHandler.HandleSelectComplete(exp);
                }

                // Default we select out sub objects/relations. So Select(d => new {Field = d.Field, Relation = new { d.Relation.Field }})
                var selectExpression = DataApiExpressionUtil.SelectDynamic(contextParameter, exp.Body, fieldExpressions, _schemaProvider);
                var node             = new DataApiNode(name, selectExpression, exp.Parameters.Any() ? exp.Parameters.First() : null, exp.Body);

                _selectContext = oldContext;
                return(node);
            }