Пример #1
0
        private static void AddFieldWithIdArgumentIfExists <TContextType>(SchemaProvider <TContextType> schema, Type contextType, Field fieldProp)
        {
            if (!fieldProp.Resolve.Type.IsEnumerableOrArray())
            {
                return;
            }
            var schemaType = fieldProp.ReturnType.SchemaType;
            var idFieldDef = schemaType.GetFields().FirstOrDefault(f => f.Name == "id");

            if (idFieldDef == null)
            {
                return;
            }

            // We need to build an anonymous type with id = RequiredField<idFieldDef.Resolve.Type>()
            // Resulting lambda is (a, p) => a.Where(b => b.Id == p.Id).First()
            // This allows us to "insert" .Select() (and .Include()) before the .First()
            var requiredFieldType = typeof(RequiredField <>).MakeGenericType(idFieldDef.Resolve.Type);
            var fieldNameAndType  = new Dictionary <string, Type> {
                { "id", requiredFieldType }
            };
            var        argTypes          = LinqRuntimeTypeBuilder.GetDynamicType(fieldNameAndType);
            var        argTypesValue     = argTypes.GetTypeInfo().GetConstructors()[0].Invoke(new Type[0]);
            var        argTypeParam      = Expression.Parameter(argTypes, $"args_{argTypes.Name}");
            Type       arrayContextType  = schemaType.ContextType;
            var        arrayContextParam = Expression.Parameter(arrayContextType, $"arrcxt_{arrayContextType.Name}");
            var        ctxId             = Expression.PropertyOrField(arrayContextParam, "Id");
            Expression argId             = Expression.PropertyOrField(argTypeParam, "id");

            argId = Expression.Property(argId, "Value"); // call RequiredField<>.Value to get the real type without a convert
            var        idBody   = Expression.MakeBinary(ExpressionType.Equal, ctxId, argId);
            var        idLambda = Expression.Lambda(idBody, new[] { arrayContextParam });
            Expression body     = ExpressionUtil.MakeCallOnQueryable("Where", new Type[] { arrayContextType }, fieldProp.Resolve, idLambda);

            body = ExpressionUtil.MakeCallOnQueryable("FirstOrDefault", new Type[] { arrayContextType }, body);
            var contextParam = Expression.Parameter(contextType, $"cxt_{contextType.Name}");
            var lambdaParams = new[] { contextParam, argTypeParam };

            body = new ParameterReplacer().ReplaceByType(body, contextType, contextParam);
            var selectionExpression = Expression.Lambda(body, lambdaParams);
            var name = fieldProp.Name.Singularize();

            if (name == null)
            {
                // If we can't singularize it just use the name plus something as GraphQL doesn't support field overloads
                name = $"{fieldProp.Name}ById";
            }
            var field = new Field(schema, name, selectionExpression, $"Return a {fieldProp.ReturnType.SchemaType.Name} by its Id", argTypesValue, new GqlTypeInfo(fieldProp.ReturnType.SchemaTypeGetter, selectionExpression.Body.Type), fieldProp.AuthorizeClaims);

            schema.AddField(field);
        }
Пример #2
0
 /// <summary>
 /// Given the type TContextType recursively create a query schema based on the public properties of the object. Schema is added into the provider schema
 /// </summary>
 /// <param name="schema">Schema tp add types to.</param>
 /// <param name="autoCreateIdArguments">If true (default), automatically create a field for any root array thats context object contains an Id property. I.e. If Actor has an Id property and the root TContextType contains IEnumerable<Actor> Actors. A root field Actor(id) will be created.</param>
 /// <param name="fieldNamer">Optionally provider a function to generate the GraphQL field name. By default this will make fields names that follow GQL style in lowerCaseCamelStyle</param>
 /// <typeparam name="TContextType"></typeparam>
 /// <returns></returns>
 public static SchemaProvider<TContextType> FromObject<TContextType>(SchemaProvider<TContextType> schema, bool autoCreateIdArguments = true, bool autoCreateEnumTypes = true, Func<MemberInfo, string> fieldNamer = null)
 {
     if (fieldNamer == null)
         fieldNamer = DefaultNamer;
     var contextType = typeof(TContextType);
     var rootFields = GetFieldsFromObject(contextType, schema, autoCreateEnumTypes, fieldNamer);
     foreach (var f in rootFields)
     {
         if (autoCreateIdArguments)
         {
             // add non-pural field with argument of ID
             AddFieldWithIdArgumentIfExists(schema, contextType, f);
         }
         schema.AddField(f);
     }
     return schema;
 }
Пример #3
0
        /// <summary>
        /// Given the type TContextType recursively create a query schema based on the public properties of the object.
        /// </summary>
        /// <param name="autoCreateIdArguments">If True, automatically create a field for any root array thats context object contains an Id property. I.e. If Actor has an Id property and the root TContextType contains IEnumerable<Actor> Actors. A root field Actor(id) will be created.</param>
        /// <typeparam name="TContextType"></typeparam>
        /// <returns></returns>
        public static SchemaProvider <TContextType> FromObject <TContextType>(bool autoCreateIdArguments = true, bool autoCreateEnumTypes = true)
        {
            var schema      = new SchemaProvider <TContextType>();
            var contextType = typeof(TContextType);
            var rootFields  = GetFieldsFromObject(contextType, schema, autoCreateEnumTypes);

            foreach (var f in rootFields)
            {
                if (autoCreateIdArguments)
                {
                    // add non-pural field with argument of ID
                    AddFieldWithIdArgumentIfExists(schema, contextType, f);
                }
                schema.AddField(f);
            }
            return(schema);
        }
Пример #4
0
        /// <summary>
        /// Given the type TContextType recursively create a query schema based on the public properties of the object.
        /// </summary>
        /// <param name="autoCreateIdArguments">If true (default), automatically create a field for any root array thats context object contains an Id property. I.e. If Actor has an Id property and the root TContextType contains IEnumerable<Actor> Actors. A root field Actor(id) will be created.</param>
        /// <param name="autoCreateIdArguments">If true (default), automatically create ENUM types for enums found in the context object graph</param>
        /// <param name="fieldNamer">Optionally provider a function to generate the GraphQL field name. By default this will make fields names that follow GQL style in lowerCaseCamelStyle</param>
        /// <typeparam name="TContextType"></typeparam>
        /// <returns></returns>
        public static SchemaProvider <TContextType> FromObject <TContextType>(bool autoCreateIdArguments = true, bool autoCreateEnumTypes = true, Func <MemberInfo, string> fieldNamer = null)
        {
            var schema = new SchemaProvider <TContextType>();

            return(FromObject(schema, autoCreateIdArguments, autoCreateEnumTypes, fieldNamer));
        }