/// 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); }
/// 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); } }