/// Given a syntax of someField { fields, to, selection, from, object } /// it will build the correct select statement private IGraphQLNode BuildDynamicSelectForObjectGraph(string query, string name, EntityGraphQLParser.EntityQueryContext context, QueryResult rootField) { var selectWasNull = false; if (selectContext == null) { selectContext = Expression.Parameter(schemaProvider.ContextType); selectWasNull = true; } if (schemaProvider.TypeHasField(selectContext.Type.Name, name, new string[0])) { name = schemaProvider.GetActualFieldName(selectContext.Type.Name, name); } try { Expression exp = rootField.LambdaExpression.Body; var oldContext = selectContext; var rootFieldParam = Expression.Parameter(exp.Type); selectContext = rootField.IsMutation ? rootFieldParam : exp; // visit child fields. Will be field or entityQueries again var fieldExpressions = context.fields.children.Select(c => Visit(c)).Where(n => n != null).ToList(); var newExp = ExpressionUtil.CreateNewExpression(selectContext, fieldExpressions, schemaProvider); var anonType = newExp.Type; // make a null check from this new expression if (!rootField.IsMutation) { newExp = Expression.Condition(Expression.MakeBinary(ExpressionType.Equal, selectContext, Expression.Constant(null)), Expression.Constant(null, anonType), newExp, anonType); } selectContext = oldContext; var t = MergeConstantParametersFromFields(rootField, fieldExpressions, rootFieldParam); var parameters = t.Item1; var constantParameterValues = t.Item2; var graphQLNode = new GraphQLNode(name, new QueryResult((ExpressionResult)newExp, parameters, constantParameterValues), (ExpressionResult)exp); if (selectWasNull) { selectContext = null; } return(graphQLNode); } catch (EntityGraphQLCompilerException ex) { throw SchemaException.MakeFieldCompileError(query, ex.Message); } }
public IGraphQLBaseNode ParseFieldSelect(ExpressionResult expContext, string name, EntityGraphQLParser.ObjectSelectionContext context) { try { IGraphQLBaseNode graphQLNode = null; if (expContext.Type.IsEnumerableOrArray()) { //var listExp = Compiler.Util.ExpressionUtil.FindDistinct(result.ExpressionResult); //if (listExp.Item1 != null) //{ // var item1 = (ExpressionResult)listExp.Item1; // item1.AddConstantParameters(result.ExpressionResult.ConstantParameters); // graphQLNode = BuildDynamicSelectOnCollection(new CompiledQueryResult(item1, result.ContextParams), name, context); // graphQLNode.SetNodeExpression((ExpressionResult)Compiler.Util.ExpressionUtil.CombineExpressions(graphQLNode.GetNodeExpression(), listExp.Item2)); //} //else //{ // graphQLNode = BuildDynamicSelectOnCollection(result, name, context); //} graphQLNode = BuildDynamicSelectOnCollection(expContext, name, context); } else { // Could be a list.First() that we need to turn into a select, or // other levels are object selection. e.g. from the top level people query I am selecting all their children { field1, etc. } // Can we turn a list.First() into and list.Select().First() var listExp = ExpressionUtil.FindIEnumerable(expContext); if (listExp.Item1 != null) { // yes we can // rebuild the ExpressionResult so we keep any ConstantParameters var item1 = (ExpressionResult)listExp.Item1; item1.AddConstantParameters(expContext.ConstantParameters); item1.AddServices(expContext.Services); graphQLNode = BuildDynamicSelectOnCollection(item1, name, context); graphQLNode.SetCombineExpression(listExp.Item2); } else { graphQLNode = BuildDynamicSelectForObjectGraph(expContext, currentExpressionContext.AsParameter(), name, context); } } return(graphQLNode); } catch (EntityGraphQLCompilerException ex) { throw SchemaException.MakeFieldCompileError($"Error compiling field {name}", ex.Message); } }
/// Given a syntax of someField { fields, to, selection, from, object } /// it will build the correct select statement private IGraphQLNode BuildDynamicSelectForObjectGraph(string query, string name, EntityGraphQLParser.EntityQueryContext context, CompiledQueryResult rootField) { var selectWasNull = false; if (selectContext == null) { selectContext = Expression.Parameter(schemaProvider.ContextType); selectWasNull = true; } if (schemaProvider.TypeHasField(selectContext.Type.Name, name, new string[0])) { name = schemaProvider.GetActualFieldName(selectContext.Type.Name, name); } try { var exp = (Expression)rootField.ExpressionResult; var oldContext = selectContext; var rootFieldParam = Expression.Parameter(exp.Type); selectContext = rootField.IsMutation ? rootFieldParam : exp; // visit child fields. Will be field or entityQueries again var fieldExpressions = context.fields.children.Select(c => Visit(c)).Where(n => n != null).ToList(); var graphQLNode = new GraphQLNode(schemaProvider, fragments, name, null, (ExpressionResult)selectContext, (rootField.IsMutation ? new ParameterExpression[] { rootFieldParam } : rootField.ContextParams.ToArray()), fieldExpressions, null); if (rootField != null && rootField.ConstantParameters != null) { graphQLNode.AddConstantParameters(rootField.ConstantParameters); } selectContext = oldContext; if (selectWasNull) { selectContext = null; } return(graphQLNode); } catch (EntityGraphQLCompilerException ex) { throw SchemaException.MakeFieldCompileError(query, ex.Message); } }
/// <summary> /// Given a syntax of { fields, to, selection, from, object } with a context /// it will build the correct select statement /// </summary> /// <param name="name"></param> /// <param name="context"></param> /// <param name="selectContext"></param> /// <returns></returns> private IGraphQLBaseNode BuildDynamicSelectForObjectGraph(ExpressionResult selectFromExp, ParameterExpression selectFromParam, string name, EntityGraphQLParser.ObjectSelectionContext context) { try { // visit child fields. Will be field or entityQueries again // These expression will be built on the element type var oldContext = currentExpressionContext; currentExpressionContext = selectFromExp; var fieldExpressions = context.children.Select(c => Visit(c)).Where(n => n != null).ToList(); currentExpressionContext = oldContext; var graphQLNode = new GraphQLQueryNode(schemaProvider, fragments, name, selectFromExp, selectFromParam, fieldExpressions, selectFromExp); return(graphQLNode); } catch (EntityGraphQLCompilerException ex) { throw SchemaException.MakeFieldCompileError($"Failed compiling field {name}", ex.Message); } }
/// <summary> /// Given a syntax of { fields, to, selection, from, object } with a context /// it will build the correct select statement /// </summary> /// <param name="name"></param> /// <param name="context"></param> /// <param name="selectContext"></param> /// <returns></returns> private IGraphQLBaseNode BuildDynamicSelectForObjectGraph(ExpressionResult selectFromExp, ParameterExpression selectFromParam, string name, EntityGraphQLParser.ObjectSelectionContext context) { try { // visit child fields. Will be field or entityQueries again // These expression will be built on the element type var oldContext = currentExpressionContext; currentExpressionContext = selectFromExp; ParameterExpression replacementParameter = null; // we might be using a service i.e. ctx => WithService((T r) => r.DoSomething(ctx.Entities.Select(f => f.Id).ToList())) // if we can we want to avoid calling that multiple times with a expression like // r.DoSomething(ctx.Entities.Select(f => f.Id).ToList()) == null ? null : new { // Field = r.DoSomething(ctx.Entities.Select(f => f.Id).ToList()).Blah // } // by wrapping the whole thing in a method that does the null check once. // This means we build the fieldExpressions on a parameter of the result type // We can only do that if it doesn't use the oldContext unless the oldContext is the root context // i.e we can't do this on a field on a type because we don't have that value as it might be a selection from an ORM bool wrapField = (oldContext.Type == schemaProvider.ContextType || oldContext.NodeType == ExpressionType.Parameter) && currentExpressionContext.Services.Any(); if (wrapField) { // replace with a parameter. The expression is compiled at execution time once replacementParameter = Expression.Parameter(selectFromExp.Type, "null_wrap"); currentExpressionContext = (ExpressionResult)replacementParameter; } var fieldExpressions = context.children.Select(c => Visit(c)).Where(n => n != null).ToList(); currentExpressionContext = oldContext; var graphQLNode = new GraphQLQueryNode(schemaProvider, fragments, name, selectFromExp, selectFromParam, fieldExpressions, selectFromExp) { IsWrapped = wrapField }; return(graphQLNode); } catch (EntityGraphQLCompilerException ex) { throw SchemaException.MakeFieldCompileError($"Failed compiling field {name}", ex.Message); } }
/// <summary> /// We compile each entityQuery with EqlCompiler and build a Select call from the fields /// </summary> /// <param name="context"></param> /// <returns></returns> public override IGraphQLBaseNode VisitEntityQuery(EntityGraphQLParser.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(".")); } if (name.IndexOf("(") > -1) { name = name.Substring(0, name.IndexOf("(")); } } try { CompiledQueryResult result = null; if (selectContext == null) { // top level are queries on the context result = EqlCompiler.Compile(query, schemaProvider, methodProvider, variables); } else { result = EqlCompiler.CompileWith(query, selectContext, schemaProvider, methodProvider, variables); } var exp = result.ExpressionResult; IGraphQLNode graphQLNode = null; if (exp.Type.IsEnumerableOrArray()) { graphQLNode = BuildDynamicSelectOnCollection(result, name, context); } else { // Could be a list.First() that we need to turn into a select, or // other levels are object selection. e.g. from the top level people query I am selecting all their children { field1, etc. } // Can we turn a list.First() into and list.Select().First() var listExp = Compiler.Util.ExpressionUtil.FindIEnumerable(result.ExpressionResult); if (listExp.Item1 != null) { // yes we can // rebuild the ExpressionResult so we keep any ConstantParameters var item1 = (ExpressionResult)listExp.Item1; item1.AddConstantParameters(result.ExpressionResult.ConstantParameters); graphQLNode = BuildDynamicSelectOnCollection(new CompiledQueryResult(item1, result.ContextParams), name, context); graphQLNode.SetNodeExpression((ExpressionResult)Compiler.Util.ExpressionUtil.CombineExpressions(graphQLNode.GetNodeExpression(), listExp.Item2)); } else { graphQLNode = BuildDynamicSelectForObjectGraph(query, name, context, result); } } // the query result may be a mutation if (result.IsMutation) { return(new GraphQLMutationNode(result, graphQLNode)); } return(graphQLNode); } catch (EntityGraphQLCompilerException ex) { throw SchemaException.MakeFieldCompileError(query, ex.Message); } }