// 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)); } } }
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)); }
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); }
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)); }
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); }
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(), }); }
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)); }
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)); }
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)); }
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); }
// 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)); }
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);
// 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); }
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)); } }
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);
// 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; }
public GraphQL(GraphQLSchema <TContext> schema = null) { _schema = schema ?? Schema; }
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);
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)); }
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);
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);
// 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);
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; })); }
// 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); }
// 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)); }
// 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)); }
// 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)); }
public static GraphQLSchema <TContext> CreateDefaultSchema(Func <TContext> creationFunc) { return(Schema = new GraphQLSchema <TContext>(creationFunc)); }
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)); }