Example #1
0
        // Transform  db => db.Entities.Where(args)  into  args => db => db.Entities.Where(args)
        public static void AddQuery <TContext, TEntity>(this GraphQLSchema <TContext> context, string name, Expression <Func <TContext, TEntity> > queryableGetter)
        {
            var queryables = typeof(TEntity).GetInterfaces().Concat(new[] { typeof(TEntity) }).Where(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IQueryable <>)).ToList();

            if (queryables.Count > 1)
            {
                throw new Exception("Types inheriting IQueryable<T> more than once are not supported.");
            }
            if (queryables.Count == 1)
            {
                var entityType = queryables[0].GetGenericArguments()[0];
                var method     = typeof(SchemaExtensions).GetMethod("AddQueryToListSimple", BindingFlags.Static | BindingFlags.NonPublic);
                var genMethod  = method.MakeGenericMethod(typeof(TContext), entityType);
                var getter     = Expression.Lambda(typeof(Func <,>).MakeGenericType(typeof(TContext), queryables[0]), queryableGetter.Body, queryableGetter.Parameters[0]);
                genMethod.Invoke(null, new object[] { context, name, getter });
            }
            else
            {
                var info = GetQueryInfo(queryableGetter);
                if (info.ResolutionType != ResolutionType.Unmodified)
                {
                    context.AddQueryInternal(name, GetFinalQueryFunc <TContext, object, IQueryable <TEntity> >(info.BaseQuery), info.ResolutionType);
                }
                else
                {
                    context.AddUnmodifiedQueryInternal(name, GetFinalQueryFunc <TContext, object, TEntity>(info.OriginalQuery));
                }
            }
        }
Example #2
0
        private static LambdaExpression GetSelector(GraphQLSchema <TContext> schema, GraphQLType gqlType, IEnumerable <ExecSelection <Info> > selections, ExpressionOptions options)
        {
            var parameter = Expression.Parameter(gqlType.CLRType, "p");
            var init      = GetMemberInit(schema, gqlType.QueryType, selections, parameter, options);

            return(Expression.Lambda(init, parameter));
        }
Example #3
0
        public static IDictionary <string, object> Execute <TArgs, TEntity>(GraphQLSchema <TContext> schema, GraphQLQuery <TContext, TArgs, TEntity> gqlQuery, Query query)
        {
            var context = schema.ContextCreator();
            var results = Execute(context, gqlQuery, query);

            (context as IDisposable)?.Dispose();
            return(results);
        }
Example #4
0
        private static ConditionalExpression GetMemberInit(GraphQLSchema <TContext> schema, Type queryType, IEnumerable <ExecSelection <Info> > selectionsEnumerable, Expression baseBindingExpr, ExpressionOptions options)
        {
            // Avoid possible multiple enumeration of selections-enumerable
            var selections = selectionsEnumerable as IList <ExecSelection <Info> > ?? selectionsEnumerable.ToList();

            // The '__typename'-field selection has to be added for queries with type conditions
            var typeConditionButNoTypeNameSelection = selections.Any() &&
                                                      selections.Any(s => s.TypeCondition != null);

            // Any '__typename' selection have to be replaced by the '__typename' selection of the target type' '__typename'-field.
            var typeNameConditionHasToBeReplaced = selections.Any(s => s.Name == TypenameFieldSelector);

            // Remove all '__typename'-selections as well as duplicates in the selections caused by fragments type condition selections.
            selections = selections
                         .Where(s => s.Name != TypenameFieldSelector)
                         .GroupBy(s => s.Name)
                         .Select(g => g.First())
                         .ToList();

            var bindings = selections
                           .Where(m => !m.SchemaField.Field().IsPost)
                           .Select(map => GetBinding(schema, map, queryType, baseBindingExpr, options))
                           .ToList();

            // Add selection for '__typename'-field of the proper type
            if (typeConditionButNoTypeNameSelection || typeNameConditionHasToBeReplaced)
            {
                // Find the base types' `__typename` field
                var graphQlType = schema.GetGQLType(baseBindingExpr.Type);
                while (graphQlType?.BaseType != null)
                {
                    graphQlType = graphQlType.BaseType;
                }
                var typeNameField = graphQlType?.OwnFields.Find(f => f.Name == TypenameFieldSelector);
                if (typeNameField != null && !typeNameField.IsPost)
                {
                    var typeNameExecSelection = new ExecSelection <Info>(
                        new SchemaField(
                            schema.Adapter.QueryTypes[graphQlType?.Name],
                            typeNameField,
                            schema.Adapter),
                        new FSharpOption <string>(typeNameField?.Name),
                        null,
                        new WithSource <ExecArgument <Info> >[] { },
                        new WithSource <ExecDirective <Info> >[] { },
                        new WithSource <ExecSelection <Info> >[] { }
                        );
                    bindings = bindings.Concat(new[] { GetBinding(schema, typeNameExecSelection, queryType, baseBindingExpr, options) }).ToList();
                }
            }

            var memberInit = Expression.MemberInit(Expression.New(queryType), bindings);

            return(NullPropagate(baseBindingExpr, memberInit));
        }
Example #5
0
        private static string CreatePropertyName(GraphQLType graphQlType, GraphQLType typeConditionType,
                                                 string fieldName)
        {
            var isObjectAbstractType = graphQlType.TypeKind == TypeKind.UNION ||
                                       graphQlType.TypeKind == TypeKind.INTERFACE;
            var isTypeConditionInterface = typeConditionType?.TypeKind == TypeKind.INTERFACE;

            return(isObjectAbstractType && !isTypeConditionInterface && typeConditionType != null
                ? GraphQLSchema <TContext> .CreatePossibleTypePropertyName(typeConditionType.Name, fieldName)
                : fieldName);
        }
Example #6
0
 public static GraphQLField Post <TField>(GraphQLSchema schema, string name, Func <TField> fieldFunc)
 {
     return(new GraphQLField
     {
         Schema = schema,
         Name = name,
         FieldCLRType = typeof(TField),
         ArgsCLRType = typeof(object),
         IsPost = true,
         PostFieldFunc = () => fieldFunc(),
     });
 }
Example #7
0
        private static ConditionalExpression GetMemberInit(GraphQLSchema <TContext> schema, GraphQLType graphQlType, IEnumerable <ExecSelection <Info> > selectionsEnumerable, Expression parameterExpression, ExpressionOptions options)
        {
            var queryType = graphQlType.QueryType;
            // Avoid possible multiple enumeration of selections-enumerable
            var selections = selectionsEnumerable as IList <ExecSelection <Info> > ?? selectionsEnumerable.ToList();

            // The '__typename'-field selection has to be added for queries with type conditions
            var typeConditionButNoTypeNameSelection = selections.Any() &&
                                                      selections.Any(s => s.TypeCondition != null);

            // Any '__typename' selection have to be replaced by the '__typename' selection of the target type' '__typename'-field.
            var typeNameConditionHasToBeReplaced = selections.Any(s => s.Name == "__typename");

            var bindingsWithPossibleDuplicates =
                selections.Where(s => !s.SchemaField.Info.Field.IsPost)
                .Where(s => s.Name != "__typename")
                .Select(s => GetBinding(schema, s, graphQlType, parameterExpression, options))
                .ToList();
            var uniqueBindingMemberNames = bindingsWithPossibleDuplicates.Select(b => b.Member.Name).Distinct();
            var bindings =
                uniqueBindingMemberNames.Select(name => bindingsWithPossibleDuplicates.First(b => b.Member.Name == name));

            // Add selection for '__typename'-field of the proper type
            if (typeConditionButNoTypeNameSelection || typeNameConditionHasToBeReplaced)
            {
                // Find the base types' `__typename` field
                var typeNameField = graphQlType?.Fields.Find(f => f.Name == "__typename");
                if (typeNameField != null && !typeNameField.IsPost)
                {
                    var typeNameExecSelection = new ExecSelection <Info>(
                        new SchemaField(
                            schema.Adapter.QueryTypes[graphQlType?.Name],
                            typeNameField,
                            schema.Adapter),
                        new FSharpOption <string>(typeNameField?.Name),
                        null,
                        new WithSource <ExecArgument <Info> >[] {},
                        new WithSource <ExecDirective <Info> >[] {},
                        new WithSource <ExecSelection <Info> >[] {}
                        );
                    bindings =
                        bindings
                        .Where(b => b.Member.Name != "__typename")
                        .Concat(new[]
                                { GetBinding(schema, typeNameExecSelection, graphQlType, parameterExpression, options) })
                        .ToList();
                }
            }

            var memberInit = Expression.MemberInit(Expression.New(queryType), bindings);

            return(NullPropagate(parameterExpression, memberInit));
        }
Example #8
0
        private static GraphQLField NewInternal <TArgs>(GraphQLSchema schema, string name, Delegate exprFunc, Type fieldCLRType, GraphQLType definingType, Delegate mutationFunc)
        {
            var isList = false;

            if (fieldCLRType.IsGenericType && TypeHelpers.IsAssignableToGenericType(fieldCLRType, typeof(IEnumerable <>)))
            {
                fieldCLRType = fieldCLRType.GetGenericArguments()[0];
                isList       = true;
            }

            return(new GraphQLField
            {
                Schema = schema,
                Name = name,
                FieldCLRType = fieldCLRType,
                DefiningType = definingType,
                ArgsCLRType = typeof(TArgs),
                IsList = isList,
                ExprFunc = exprFunc,
                MutationFunc = mutationFunc,
                IsMutation = mutationFunc != null,
            });
        }
 private static void InitializeNullRefSchema(GraphQLSchema<EfContext> schema)
 {
     var nullRef = schema.AddType<NullRef>();
     nullRef.AddField(n => n.Id);
 }
        private static void InitializeCharacterSchema(GraphQLSchema<EfContext> schema)
        {
            schema.AddType<Character>().AddAllFields();
            schema.AddType<Human>().AddAllFields();
            schema.AddType<Stormtrooper>().AddAllFields();
            schema.AddType<Droid>().AddAllFields();
            schema.AddType<Vehicle>().AddAllFields();

            schema.AddField("hero", new { id = 0 }, (db, args) => db.Heros.SingleOrDefault(h => h.Id == args.id));
            schema.AddListField("heros", db => db.Heros.AsQueryable());
        }
        private static string GetAbcPostField() => "easy as 123"; // mimic an in-memory function

        private static void InitializeAccountSchema(GraphQLSchema<EfContext> schema)
        {
            var account = schema.AddType<Account>();
            account.AddField(a => a.Id);
            account.AddField(a => a.Name);
            account.AddField(a => a.Paid);
            account.AddField(a => a.SomeGuid);
            account.AddField(a => a.ByteArray);
            account.AddListField(a => a.Users);
            account.AddListField("activeUsers", (db, a) => a.Users.Where(u => u.Active));
            account.AddListField("usersWithActive", new { active = false }, (db, args, a) => a.Users.Where(u => u.Active == args.active));
            account.AddField("firstUserWithActive", new { active = false }, (db, args, a) => a.Users.FirstOrDefault(u => u.Active == args.active));

            schema.AddField("account", new { id = 0 }, (db, args) => db.Accounts.FirstOrDefault(a => a.Id == args.id));
            schema.AddField
                ("accountPaidBy", new { paid = default(DateTime) },
                    (db, args) => db.Accounts.AsQueryable().FirstOrDefault(a => a.PaidUtc <= args.paid));
            schema.AddListField("accountsByGuid", new { guid = Guid.Empty },
                    (db, args) => db.Accounts.AsQueryable().Where(a => a.SomeGuid == args.guid));
        }
        private static void InitializeMutationSchema(GraphQLSchema<EfContext> schema)
        {
            var mutate = schema.AddType<MutateMe>();
            mutate.AddAllFields();

            schema.AddField("mutateMes", new { id = 0 }, (db, args) => db.MutateMes.AsQueryable().FirstOrDefault(a => a.Id == args.id));
            schema.AddMutation("mutate",
                new { id = 0, newVal = 0 },
                (db, args) =>
                {
                    var mutateMe = db.MutateMes.First(m => m.Id == args.id);
                    mutateMe.Value = args.newVal;
                    db.SaveChanges();
                },
                (db, args) => db.MutateMes.AsQueryable().FirstOrDefault(a => a.Id == args.id));
            schema.AddMutation("addMutate",
                new { newVal = 0 },
                (db, args) =>
                {
                    var newMutate = new MutateMe { Value = args.newVal };
                    db.MutateMes.Add(newMutate);
                    db.SaveChanges();
                    return newMutate.Id;
                },
                (db, args, id) => db.MutateMes.AsQueryable().FirstOrDefault(a => a.Id == id));
        }
Example #13
0
 public void IncompleteSchemaCantBeQueried()
 {
     var schema = new GraphQLSchema<object>(() => null);
     Assert.Throws<InvalidOperationException>(() => new GraphQL<object>(schema).ExecuteQuery("query users { id }"));
 }
        private static void InitializeUserSchema(GraphQLSchema<EfContext> schema)
        {
            var user = schema.AddType<User>();
            user.AddField(u => u.Id);
            user.AddField(u => u.Name);
            user.AddField(u => u.Account);
            user.AddField(u => u.NullRef);
            user.AddField("total", (db, u) => db.Users.Count());
            user.AddField("accountPaid", (db, u) => u.Account.Paid);
            user.AddPostField("abc", () => GetAbcPostField());
            user.AddPostField("sub", () => new Sub { Id = 1 });

            schema.AddType<Sub>().AddField(s => s.Id);
            schema.AddListField("users", db => db.Users);
            schema.AddField("user", new { id = 0 }, (db, args) => db.Users.FirstOrDefault(u => u.Id == args.id));
        }
Example #15
0
        private static IDictionary <string, object> MapResults(object queryObject, IEnumerable <ExecSelection <Info> > selections, GraphQLSchema <TContext> schema)
        {
            if (queryObject == null) // TODO: Check type non-null and throw exception
            {
                return(null);
            }
            var dict = new Dictionary <string, object>();
            var type = queryObject.GetType();

            foreach (var map in selections)
            {
                var key   = map.Name;
                var field = map.SchemaField.Field();
                var obj   = field.IsPost
                    ? field.PostFieldFunc()
                    : type.GetProperty(field.Name).GetGetMethod().Invoke(queryObject, new object[] { });


                // Filter fields for selections with type conditions - the '__typename'-property has to be present.
                if (map.TypeCondition != null)
                {
                    // The selection has a type condition, i.e. the result has a type with included types.
                    // The result contains all fields of all included types and has to be filtered.
                    // Sample:
                    //
                    // Types:
                    //      - Character     [name: string]
                    //      - Human         [height: float]           extends Character
                    //      - Stormtrooper  [specialization: string]  extends Human
                    //      - Droid         [orimaryFunction: string] extends Character
                    //
                    // Sample query:
                    //      query { heros { name, ... on Human { height }, ... on Stormtrooper { specialization }, ... on Droid { primaryFunction } } }
                    //
                    // The ExecSelection for 'height', 'specialization' and 'primaryFunction' have a type condition with the following type names:
                    //      - height:           Human
                    //      - specialization:   Stormtrooper
                    //      - primaryFunction:  Droid
                    //
                    // To filter the result properly, we have to consider the following cases:
                    // - Human:
                    //      - Include: 'name', 'height'
                    //      - Exclude: 'specialization', 'primaryFunction'
                    //  => (1) Filter results of selections with a type-condition-name != result.__typename
                    //
                    //  - Stormtrooper
                    //      - Include: 'name', 'height', and 'specialization'
                    //      - Exclude: 'primaryFunction'
                    // => Same as Human (1), but:
                    //  => (2) include results of selections with the same type-condition-name of any ancestor-type.
                    //
                    var selectionConditionTypeName = map.TypeCondition.Value?.TypeName;
                    var typenameProp   = type.GetRuntimeProperty(TypenameFieldSelector);
                    var resultTypeName = (string)typenameProp?.GetValue(queryObject);

                    // (1) type-condition-name != result.__typename
                    if (selectionConditionTypeName != resultTypeName)
                    {
                        // (2) Check ancestor types
                        var resultGraphQlType = schema.Types.FirstOrDefault(t => t.Name == resultTypeName);
                        if (resultGraphQlType != null && !IsEqualToAnyAncestorType(resultGraphQlType, selectionConditionTypeName))
                        {
                            continue;
                        }
                    }
                }

                if (obj == null)
                {
                    dict.Add(key, null);
                    continue;
                }

                if (field.IsPost && map.Selections.Any())
                {
                    var selector = GetSelector(schema, field.Type, map.Selections.Values(), new ExpressionOptions(null, castAssignment: true, nullCheckLists: true, typeCheckInheritance: true));
                    obj = selector.Compile().DynamicInvoke(obj);
                }

                // Due to type conditions a key might occur multiple times
                if (map.Selections.Any())
                {
                    var listObj = obj as IEnumerable <object>;
                    if (listObj != null && !dict.ContainsKey(key))
                    {
                        dict.Add(key, listObj.Select(o => MapResults(o, map.Selections.Values(), schema)).ToList());
                    }
                    else if (obj != null && !dict.ContainsKey(key))
                    {
                        dict.Add(key, MapResults(obj, map.Selections.Values(), schema));
                    }
                    else
                    {
                        throw new Exception("Shouldn't be here");
                    }
                }
                else
                {
                    if (!dict.ContainsKey(key))
                    {
                        dict.Add(key, obj);
                    }
                }
            }
            return(dict);
        }
Example #16
0
 // Transform  db => db.Entities.Where(args)  into  args => db => db.Entities.Where(args)
 public static GraphQLFieldBuilder <TContext, TEntity> AddListField <TContext, TEntity>(
     this GraphQLSchema <TContext> context, string name,
     Expression <Func <TContext, IEnumerable <TEntity> > > queryableGetter)
 {
     return(context.AddFieldInternal(name, GetFinalQueryFunc <TContext, object, IEnumerable <TEntity> >(queryableGetter), ResolutionType.ToList));
 }
Example #17
0
 public static GraphQLField NewMutation <TContext, TArgs, TMutReturn>(GraphQLSchema schema, string name, Func <TArgs, TMutReturn, LambdaExpression> exprFunc, Type fieldCLRType, GraphQLType definingType, Func <TContext, TArgs, TMutReturn> mutationFunc)
 => NewInternal <TArgs>(schema, name, exprFunc, fieldCLRType, definingType, mutationFunc);
Example #18
0
        // Transform  (db, args) => db.Entities.Where(args)  into  args => db => db.Entities.Where(args)
        private static void AddQueryToListArgs <TContext, TArgs, TEntity>(this GraphQLSchema <TContext> context, string name, Expression <Func <TContext, TArgs, IQueryable <TEntity> > > queryableGetter)
        {
            var innerLambda = Expression.Lambda <Func <TContext, IQueryable <TEntity> > >(queryableGetter.Body, queryableGetter.Parameters[0]);

            context.AddQueryInternal(name, GetFinalQueryFunc <TContext, TArgs, IQueryable <TEntity> >(innerLambda, queryableGetter.Parameters[1]), ResolutionType.ToList);
        }
Example #19
0
        public static object Execute
            (GraphQLSchema <TContext> schema, TContext context, GraphQLField field, ExecSelection <Info> query)
        {
            var mutReturn = field.RunMutation(context, query.Arguments.Values());

            var queryableFuncExpr = field.GetExpression(query.Arguments.Values(), mutReturn);
            var replaced          = (LambdaExpression)ParameterReplacer.Replace(queryableFuncExpr, queryableFuncExpr.Parameters[0], GraphQLSchema <TContext> .DbParam);

            // sniff queryable provider to determine how selector should be built
            var dummyQuery        = replaced.Compile().DynamicInvoke(context, null);
            var queryType         = dummyQuery.GetType();
            var expressionOptions = schema.GetOptionsForQueryable(queryType);

            if (expressionOptions.UseBaseType)
            {
                queryType         = queryType.BaseType;
                expressionOptions = schema.GetOptionsForQueryable(queryType);
            }

            var queryExecSelections = query.Selections.Values();
            var selector            = GetSelector(schema, field.Type, queryExecSelections, expressionOptions);

            if (field.ResolutionType != ResolutionType.Unmodified)
            {
                var selectorExpr = Expression.Quote(selector);

                // TODO: This should be temporary - queryable and enumerable should both work
                var body = replaced.Body;
                if (body.NodeType == ExpressionType.Convert)
                {
                    body = ((UnaryExpression)body).Operand;
                }

                var call        = Expression.Call(typeof(Queryable), "Select", new[] { field.Type.CLRType, field.Type.QueryType }, body, selectorExpr);
                var expr        = Expression.Lambda(call, GraphQLSchema <TContext> .DbParam);
                var transformed = expr.Compile().DynamicInvoke(context);

                object results;
                switch (field.ResolutionType)
                {
                case ResolutionType.Unmodified:
                    throw new Exception("Queries cannot have unmodified resolution. May change in the future.");

                case ResolutionType.ToList:
                    var list = GenericQueryableCall <IEnumerable <object> >(transformed, q => q.ToList());
                    results = list.Select(o => MapResults(o, queryExecSelections, schema)).ToList();
                    break;

                case ResolutionType.FirstOrDefault:
                    var fod = GenericQueryableCall(transformed, q => q.FirstOrDefault());
                    results = MapResults(fod, queryExecSelections, schema);
                    break;

                case ResolutionType.First:
                    var first = GenericQueryableCall(transformed, q => q.FirstOrDefault());
                    results = MapResults(first, queryExecSelections, schema);
                    break;

                default:
                    throw new ArgumentOutOfRangeException();
                }

                return(results);
            }
            else
            {
                var invocation = Expression.Invoke(selector, replaced.Body);
                var expr       = Expression.Lambda(invocation, GraphQLSchema <TContext> .DbParam);
                var result     = expr.Compile().DynamicInvoke(context);
                return(MapResults(result, queryExecSelections, schema));
            }
        }
Example #20
0
 public static GraphQLFieldBuilder <TContext, TEntity> AddListField <TContext, TArgs, TEntity>(this GraphQLSchema <TContext> context, string name, TArgs argObj, Expression <Func <TContext, TArgs, IEnumerable <TEntity> > > queryableGetter)
 => AddListField(context, name, queryableGetter);
Example #21
0
        // Transform  db => db.Entities.Where(args)  into  args => db => db.Entities.Where(args)
        public static GraphQLFieldBuilder <TContext, TEntity> AddField <TContext, TEntity>(this GraphQLSchema <TContext> context, string name, Expression <Func <TContext, TEntity> > queryableGetter)
        {
            var info = GetQueryInfo(queryableGetter);

            if (info.ResolutionType != ResolutionType.Unmodified)
            {
                return(context.AddFieldInternal(name, GetFinalQueryFunc <TContext, object, IEnumerable <TEntity> >(info.BaseQuery), info.ResolutionType));
            }
            return(context.AddUnmodifiedFieldInternal(name, GetFinalQueryFunc <TContext, object, TEntity>(info.OriginalQuery)));
        }
 internal GraphQLTypeBuilder(GraphQLSchema <TContext> schema, GraphQLType type)
 {
     _schema = schema;
     _type   = type;
 }
Example #23
0
 public GraphQL(GraphQLSchema <TContext> schema = null)
 {
     _schema = schema ?? Schema;
 }
Example #24
0
 public static GraphQLFieldBuilder <TContext, TEntity> AddMutation <TContext, TArgs, TEntity>(this GraphQLSchema <TContext> context, string name, TArgs argObj, Expression <Func <TContext, TArgs, TEntity> > queryableGetter, Action <TContext, TArgs> mutation)
 => AddMutation(context, name, argObj, mutation, queryableGetter);
Example #25
0
        private static MemberBinding GetBinding(GraphQLSchema <TContext> schema, ExecSelection <Info> map, Type toType, Expression baseBindingExpr, ExpressionOptions options)
        {
            var field          = map.SchemaField.Field();
            var needsTypeCheck = baseBindingExpr.Type != field.DefiningType.CLRType;
            var toMember       = toType.GetProperty(map.SchemaField.FieldName);
            // expr is form of: (context, entity) => entity.Field
            var expr = field.GetExpression(map.Arguments.Values());

            // The field might be defined on sub-types of the specified type, so add a type cast if necessary.
            // If appropriate, `entity.Field` becomes `(entity as TFieldDefiningType).Field`
            var typeCastedBaseExpression = needsTypeCheck ? Expression.TypeAs(baseBindingExpr, field.DefiningType.CLRType) : baseBindingExpr;

            // Replace (entity) with baseBindingExpr, note expression is no longer a LambdaExpression
            // `(context, entity) => entity.Field` becomes `someOtherEntity.Entity.Field` where baseBindingExpr is `someOtherEntity.Entity`
            var replacedBase = ParameterReplacer.Replace(expr.Body, expr.Parameters[1], typeCastedBaseExpression);

            // If the entity has to be casted, add a type check:
            // `(someOtherEntity.Entity as TFieldDefininingType).Field` becomes `(someOtherEntity.Entity is TFieldDefininingType) ? (someOtherEntity.Entity as TFieldDefininingType).Field : null`
            if (needsTypeCheck && options.TypeCheckInheritance)
            {
                // The expression type has to be nullable
                if (replacedBase.Type.IsValueType)
                {
                    replacedBase = Expression.Convert(replacedBase, typeof(Nullable <>).MakeGenericType(replacedBase.Type));
                }

                var typeCheck  = Expression.TypeIs(baseBindingExpr, field.DefiningType.CLRType);
                var nullResult = Expression.Constant(null, replacedBase.Type);
                replacedBase = Expression.Condition(typeCheck, replacedBase, nullResult);
            }

            // This just makes sure that the (context) parameter is the same as the one used by the whole query
            var replacedContext = ParameterReplacer.Replace(replacedBase, expr.Parameters[0], GraphQLSchema <TContext> .DbParam);

            // If there aren't any children, then we can assume that this is a scalar entity and we don't have to map child fields
            if (!map.Selections.Any())
            {
                if (options.CastAssignment && expr.Body.Type != toMember.PropertyType)
                {
                    replacedContext = Expression.Convert(replacedContext, toMember.PropertyType);
                }
                return(Expression.Bind(toMember, replacedContext));
            }

            // If binding a single entity, just use the already built selector expression (replaced context)
            // Otherwise, if binding to a list, introduce a new parameter that will be used in a call to .Select
            var listParameter = Expression.Parameter
                                    (field.Type.CLRType, field.Type.CLRType.Name.Substring(0, 1).ToLower()); // TODO: Name conflicts in expressions?
            var bindChildrenTo = map.SchemaField.Field().IsList ? listParameter : replacedContext;

            // Now that we have our new binding parameter, build the tree for the rest of the children
            var memberInit = GetMemberInit(schema, field.Type.QueryType, map.Selections.Values(), bindChildrenTo, options);

            // For single entities, we're done and we can just bind to the memberInit expression
            if (!field.IsList)
            {
                return(Expression.Bind(toMember, memberInit));
            }

            // However for lists, we need to check for null and call .Select() and .ToList() first
            var selectLambda = Expression.Lambda(memberInit, listParameter);
            var call         = Expression.Call(typeof(Enumerable), "Select", new[] { field.Type.CLRType, field.Type.QueryType }, replacedContext, selectLambda);
            var toList       = Expression.Call(typeof(Enumerable), "ToList", new[] { field.Type.QueryType }, call);

            return(Expression.Bind(toMember, options.NullCheckLists ? (Expression)NullPropagate(replacedContext, toList) : toList));
        }
Example #26
0
 public static GraphQLField New <TArgs>(GraphQLSchema schema, string name, Func <TArgs, LambdaExpression> exprFunc, Type fieldCLRType, GraphQLType definingType)
 => NewInternal <TArgs>(schema, name, exprFunc, fieldCLRType, definingType, null);
Example #27
0
 public static GraphQLFieldBuilder <TContext, TEntity> AddListMutation <TContext, TArgs, TEntity, TMutReturn>(this GraphQLSchema <TContext> context, string name, TArgs argObj, Func <TContext, TArgs, TMutReturn> mutation, Expression <Func <TContext, TArgs, TMutReturn, IEnumerable <TEntity> > > queryableGetter)
 => AddListMutation(context, name, queryableGetter, mutation);
Example #28
0
 // This overload is provided to the user so they can shape TArgs with an anonymous type and rely on type inference for type parameters
 // e.g.  AddQuery("user", new { id = 0 }, (db, args) => db.Users.Where(u => u.Id == args.id));
 public static void AddQuery <TContext, TArgs, TEntity>(this GraphQLSchema <TContext> context, string name, TArgs argObj, Expression <Func <TContext, TArgs, TEntity> > queryableGetter)
 => AddQuery(context, name, queryableGetter);
Example #29
0
        public static GraphQLFieldBuilder <TContext, TEntity> AddListMutation <TContext, TArgs, TEntity>(this GraphQLSchema <TContext> context, string name, TArgs argObj, Action <TContext, TArgs> mutation, Expression <Func <TContext, TArgs, IEnumerable <TEntity> > > queryableGetter)
        {
            var transformed = Expression.Lambda <Func <TContext, TArgs, object, IEnumerable <TEntity> > >(queryableGetter.Body, queryableGetter.Parameters[0], queryableGetter.Parameters[1], Expression.Parameter(typeof(object), "mutRet"));

            return(AddListMutation(context, name, transformed, (db, args) => { mutation(db, args); return null; }));
        }
Example #30
0
 // Transform  db => db.Entities.Where(args)  into  args => db => db.Entities.Where(args)
 private static void AddQueryToListSimple <TContext, TEntity>(this GraphQLSchema <TContext> context, string name, Expression <Func <TContext, IQueryable <TEntity> > > queryableGetter)
 {
     context.AddQueryInternal(name, GetFinalQueryFunc <TContext, object, IQueryable <TEntity> >(queryableGetter), ResolutionType.ToList);
 }
Example #31
0
        // Transform  (db, args) => db.Entities.Where(args)  into  args => db => db.Entities.Where(args)
        private static GraphQLFieldBuilder <TContext, TEntity> AddListField <TContext, TArgs, TEntity>(this GraphQLSchema <TContext> context, string name, Expression <Func <TContext, TArgs, IEnumerable <TEntity> > > queryableGetter)
        {
            var innerLambda = Expression.Lambda <Func <TContext, IEnumerable <TEntity> > >(queryableGetter.Body, queryableGetter.Parameters[0]);

            return(context.AddFieldInternal(name, GetFinalQueryFunc <TContext, TArgs, IEnumerable <TEntity> >(innerLambda, queryableGetter.Parameters[1]), ResolutionType.ToList));
        }
Example #32
0
        // Transform  (db, args, mut) => db.Entities.Where(args)  into  args => db => db.Entities.Where(args)
        private static GraphQLFieldBuilder <TContext, TEntity> AddListMutation <TContext, TArgs, TEntity, TMutReturn>(this GraphQLSchema <TContext> context, string name, Expression <Func <TContext, TArgs, TMutReturn, IEnumerable <TEntity> > > queryableGetter, Func <TContext, TArgs, TMutReturn> mutation)
        {
            var innerLambda = Expression.Lambda <Func <TContext, IEnumerable <TEntity> > >(queryableGetter.Body, queryableGetter.Parameters[0]);

            return(context.AddMutationInternal(name, GetFinalMutationFunc <TContext, TArgs, TMutReturn, IEnumerable <TEntity> >(innerLambda, queryableGetter.Parameters[1], queryableGetter.Parameters[2]), ResolutionType.ToList, mutation));
        }
Example #33
0
        // Transform  (db, args, mut) => db.Entities.First(args)  into  args => db => db.Entities.First(args)
        private static GraphQLFieldBuilder <TContext, TEntity> AddMutation <TContext, TArgs, TEntity, TMutReturn>(this GraphQLSchema <TContext> context, string name, Expression <Func <TContext, TArgs, TMutReturn, TEntity> > queryableGetter, Func <TContext, TArgs, TMutReturn> mutation)
        {
            var innerLambda = Expression.Lambda <Func <TContext, TEntity> >(queryableGetter.Body, queryableGetter.Parameters[0]);
            var info        = GetQueryInfo(innerLambda);

            if (info.ResolutionType != ResolutionType.Unmodified)
            {
                return(context.AddMutationInternal(name, GetFinalMutationFunc <TContext, TArgs, TMutReturn, IEnumerable <TEntity> >(info.BaseQuery, queryableGetter.Parameters[1], queryableGetter.Parameters[2]), info.ResolutionType, mutation));
            }
            return(context.AddUnmodifiedMutationInternal(name, GetFinalMutationFunc <TContext, TArgs, TMutReturn, TEntity>(info.OriginalQuery, queryableGetter.Parameters[1], queryableGetter.Parameters[2]), mutation));
        }
Example #34
0
 public static GraphQLSchema <TContext> CreateDefaultSchema(Func <TContext> creationFunc)
 {
     return(Schema = new GraphQLSchema <TContext>(creationFunc));
 }
Example #35
0
        private static void InitializeMutationSchema(GraphQLSchema<MemContext> schema)
        {
            var mutate = schema.AddType<MutateMe>();
            mutate.AddAllFields();

            schema.AddField("mutateMes", new { id = 0 }, (db, args) => db.MutateMes.AsQueryable().FirstOrDefault(a => a.Id == args.id));
            schema.AddMutation("mutate",
                new { id = 0, newVal = 0 },
                (db, args) =>
                {
                    var mutateMe = db.MutateMes.First(m => m.Id == args.id);
                    mutateMe.Value = args.newVal;
                },
                (db, args) => db.MutateMes.AsQueryable().FirstOrDefault(a => a.Id == args.id));
            schema.AddMutation("addMutate",
                new { newVal = 0 },
                (db, args) =>
                {
                    var newMutate = new MutateMe { Value = args.newVal };
                    db.MutateMes.Add(newMutate);
                    // simulate Id being set by database
                    newMutate.Id = db.MutateMes.Max(m => m.Id) + 1;
                    return newMutate.Id;
                },
                (db, args, id) => db.MutateMes.AsQueryable().FirstOrDefault(a => a.Id == id));
        }