/// Given a syntax of someCollection { fields, to, selection, from, object }
        /// it will build a select assuming 'someCollection' is an IEnumerables
        private IGraphQLNode BuildDynamicSelectOnCollection(CompiledQueryResult queryResult, string name, EntityGraphQLParser.EntityQueryContext context)
        {
            var elementType      = queryResult.BodyType.GetEnumerableOrArrayType();
            var contextParameter = Expression.Parameter(elementType, $"param_{elementType}");

            var exp = queryResult.ExpressionResult;

            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 gqlNode = new GraphQLNode(schemaProvider, fragments, name, null, exp, queryResult.ContextParams, fieldExpressions, contextParameter);

            selectContext = oldContext;

            return(gqlNode);
        }
示例#2
0
        public override GraphQLOperation VisitGqlTypeDef(EntityGraphQLParser.GqlTypeDefContext context)
        {
            var argName  = context.gqlVar().GetText().TrimStart('$');
            var isArray  = context.arrayType != null;
            var type     = isArray ? context.arrayType.type.GetText() : context.type.GetText();
            var required = context.required != null;
            CompiledQueryResult defaultValue = null;

            if (context.defaultValue != null)
            {
                defaultValue = EqlCompiler.CompileWith(context.defaultValue.GetText(), null, schemaProvider, null, variables);
            }

            if (required && !variables.ContainsKey(argName) && defaultValue == null)
            {
                throw new QueryException($"Missing required variable '{argName}' on query '{this.operation.Name}'");
            }

            this.operation.AddArgument(argName, type, isArray, required, defaultValue != null ? defaultValue.ExpressionResult : null);

            return(this.operation);
        }
示例#3
0
        public object Execute(params object[] args)
        {
            // run the mutation to get the context for the query select
            var mutation = (MutationResult)this.result.ExpressionResult;
            var result   = mutation.Execute(args);

            if (typeof(LambdaExpression).IsAssignableFrom(result.GetType()))
            {
                var mutationLambda       = (LambdaExpression)result;
                var mutationContextParam = mutationLambda.Parameters.First();
                var mutationExpression   = mutationLambda.Body;

                // this willtypically be similar to
                // db => db.Entity.Where(filter) or db => db.Entity.First(filter)
                // i.e. they'll be returning a list of items or a specific item
                // We want to take the field selection from the GraphQL query and add a LINQ Select() onto the expression
                // In the case of a First() we need to insert that select before the first
                // This is all to have 1 nice expression that can work with ORMs (like EF)
                // E.g  we want db => db.Entity.Select(e => new {name = e.Name, ...}).First(filter)
                // we dot not want db => new {name = db.Entity.First(filter).Name, ...})

                var selectParam = graphQLNode.Parameters.First();

                if (!mutationLambda.ReturnType.IsEnumerableOrArray())
                {
                    if (mutationExpression.NodeType == ExpressionType.Call)
                    {
                        var call = (MethodCallExpression)mutationExpression;
                        if (call.Method.Name == "First" || call.Method.Name == "FirstOrDefault" || call.Method.Name == "Last" || call.Method.Name == "LastOrDefault")
                        {
                            var baseExp = call.Arguments.First();
                            if (call.Arguments.Count == 2)
                            {
                                // move the fitler to a Where call
                                var filter = call.Arguments.ElementAt(1);
                                baseExp = ExpressionUtil.MakeExpressionCall(new [] { typeof(Queryable), typeof(Enumerable) }, "Where", new Type[] { selectParam.Type }, baseExp, filter);
                            }

                            // build select
                            var selectExp = ExpressionUtil.MakeExpressionCall(new [] { typeof(Queryable), typeof(Enumerable) }, "Select", new Type[] { selectParam.Type, graphQLNode.GetNodeExpression().Type }, baseExp, Expression.Lambda(graphQLNode.GetNodeExpression(), selectParam));

                            // add First/Last back
                            var firstExp = ExpressionUtil.MakeExpressionCall(new [] { typeof(Queryable), typeof(Enumerable) }, call.Method.Name, new Type[] { selectExp.Type.GetGenericArguments()[0] }, selectExp);

                            // we're done
                            graphQLNode.SetNodeExpression(firstExp);
                        }
                    }
                    else
                    {
                        // if they just return a constant I.e the entity they just updated. It comes as a memebr access constant
                        if (mutationLambda.Body.NodeType == ExpressionType.MemberAccess)
                        {
                            var me = (MemberExpression)mutationLambda.Body;
                            if (me.Expression.NodeType == ExpressionType.Constant)
                            {
                                graphQLNode.AddConstantParameter(Expression.Parameter(me.Type), Expression.Lambda(me).Compile().DynamicInvoke());
                            }
                        }
                        else if (mutationLambda.Body.NodeType == ExpressionType.Constant)
                        {
                            var ce = (ConstantExpression)mutationLambda.Body;
                            graphQLNode.AddConstantParameter(Expression.Parameter(ce.Type), ce.Value);
                        }
                    }
                }
                else
                {
                    var exp = ExpressionUtil.MakeExpressionCall(new [] { typeof(Queryable), typeof(Enumerable) }, "Select", new Type[] { selectParam.Type, graphQLNode.GetNodeExpression().Type }, mutationExpression, Expression.Lambda(graphQLNode.GetNodeExpression(), selectParam));
                    graphQLNode.SetNodeExpression(exp);
                }

                // make sure we use the right parameter
                graphQLNode.Parameters[0] = mutationContextParam;
                var executionArg = args[0];
                result = graphQLNode.Execute(executionArg);
                return(result);
            }
            // run the query select
            result = graphQLNode.Execute(result);
            return(result);
        }
示例#4
0
 public GraphQLMutationNode(CompiledQueryResult result, IGraphQLNode graphQLNode)
 {
     this.result      = result;
     this.graphQLNode = graphQLNode;
     Fields           = new List <IGraphQLNode>();
 }
        /// <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);
            }
        }
        /// 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);
            }
        }
        public object Execute(params object[] args)
        {
            var allArgs = new List <object>(args);

            // run the mutation to get the context for the query select
            var mutation = (MutationResult)this.result.ExpressionResult;
            var result   = mutation.Execute(args);

            if (typeof(LambdaExpression).IsAssignableFrom(result.GetType()))
            {
                var mutationLambda       = (LambdaExpression)result;
                var mutationContextParam = mutationLambda.Parameters.First();
                var mutationExpression   = mutationLambda.Body;

                // this willtypically be similar to
                // db => db.Entity.Where(filter) or db => db.Entity.First(filter)
                // i.e. they'll be returning a list of items or a specific item
                // We want to take the field selection from the GraphQL query and add a LINQ Select() onto the expression
                // In the case of a First() we need to insert that select before the first
                // This is all to have 1 nice expression that can work with ORMs (like EF)
                // E.g  we want db => db.Entity.Select(e => new {name = e.Name, ...}).First(filter)
                // we dot not want db => new {name = db.Entity.First(filter).Name, ...})

                var selectParam = graphQLNode.Parameters.First();

                if (!mutationLambda.ReturnType.IsEnumerableOrArray() && mutationExpression.NodeType == ExpressionType.Call)
                {
                    var call = (MethodCallExpression)mutationExpression;
                    if (call.Method.Name == "First" || call.Method.Name == "FirstOrDefault" || call.Method.Name == "Last" || call.Method.Name == "LastOrDefault")
                    {
                        var baseExp = call.Arguments.First();
                        if (call.Arguments.Count == 2)
                        {
                            // move the fitler to a Where call
                            var filter = call.Arguments.ElementAt(1);
                            baseExp = ExpressionUtil.MakeExpressionCall(new [] { typeof(Queryable), typeof(Enumerable) }, "Where", new Type[] { selectParam.Type }, baseExp, filter);
                        }

                        // build select
                        var selectExp = ExpressionUtil.MakeExpressionCall(new [] { typeof(Queryable), typeof(Enumerable) }, "Select", new Type[] { selectParam.Type, graphQLNode.GetNodeExpression().Type }, baseExp, Expression.Lambda(graphQLNode.GetNodeExpression(), selectParam));

                        // add First/Last back
                        var firstExp = ExpressionUtil.MakeExpressionCall(new [] { typeof(Queryable), typeof(Enumerable) }, call.Method.Name, new Type[] { selectExp.Type.GetGenericArguments()[0] }, selectExp);

                        // we're done
                        graphQLNode.SetNodeExpression((ExpressionResult)firstExp);
                    }
                    else
                    {
                        throw new QueryException($"Mutation {Name} has invalid return type of {result.GetType()}. Please return Expression<Func<TConext, TEntity>> or Expression<Func<TConext, IEnumerable<TEntity>>>");
                    }
                }
                else
                {
                    var exp = Expression.Call(typeof(Queryable), "Select", new Type[] { selectParam.Type, graphQLNode.GetNodeExpression().Type }, mutationExpression, Expression.Lambda(graphQLNode.GetNodeExpression(), selectParam));
                    graphQLNode.SetNodeExpression((ExpressionResult)exp);
                }

                // make sure we use the right parameter
                graphQLNode.Parameters[0] = mutationContextParam;
                result = graphQLNode.Execute(args[0]);
                return(result);
            }
            // run the query select
            result = graphQLNode.Execute(result);
            return(result);
        }