Esempio n. 1
0
        public static List <Field> GetFieldsFromObject <TContextType>(Type type, MappedSchemaProvider <TContextType> schema, bool createEnumTypes, bool createNewComplexTypes = true)
        {
            var fields = new List <Field>();
            // cache fields/properties
            var param = Expression.Parameter(type);

            if (type.IsArray || type.IsEnumerableOrArray())
            {
                return(fields);
            }

            foreach (var prop in type.GetProperties())
            {
                var f = ProcessFieldOrProperty(prop, prop.PropertyType, param, schema, createEnumTypes, createNewComplexTypes);
                if (f != null)
                {
                    fields.Add(f);
                }
            }
            foreach (var prop in type.GetFields())
            {
                var f = ProcessFieldOrProperty(prop, prop.FieldType, param, schema, createEnumTypes, createNewComplexTypes);
                if (f != null)
                {
                    fields.Add(f);
                }
            }
            return(fields);
        }
Esempio n. 2
0
        private static void AddFieldWithIdArgumentIfExists <TContextType>(MappedSchemaProvider <TContextType> schema, Type contextType, Field fieldProp)
        {
            if (!fieldProp.Resolve.Type.IsEnumerableOrArray())
            {
                return;
            }
            var schemaType = schema.Type(fieldProp.ReturnTypeClrSingle);
            // Find the first field named "id" or "<fieldProp.Name>Id" to turn into a field with arguments
            var idFieldDef = schemaType.GetFields().FirstOrDefault(f => f.Name.ToLower() == "id" || f.Name.ToLower() == $"{fieldProp.Name.ToLower()}id");

            if (idFieldDef == null)
            {
                return;
            }

            // Save a little bit of typing and clean things up.
            var idFieldName = idFieldDef.Name;

            // 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> {
                { idFieldName, requiredFieldType }
            };
            var  argTypes          = LinqRuntimeTypeBuilder.GetDynamicType(fieldNameAndType);
            var  argTypesValue     = argTypes.GetTypeInfo().GetConstructors()[0].Invoke(new Type[0]);
            var  argTypeParam      = Expression.Parameter(argTypes);
            Type arrayContextType  = schema.Type(fieldProp.ReturnTypeClrSingle).ContextType;
            var  arrayContextParam = Expression.Parameter(arrayContextType);

            var        ctxId = Expression.PropertyOrField(arrayContextParam, $"{char.ToUpper(idFieldName[0])}{idFieldName.Substring(1)}");
            Expression argId = Expression.PropertyOrField(argTypeParam, idFieldName);

            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.MakeExpressionCall(new[] { typeof(Queryable), typeof(Enumerable) }, "Where", new Type[] { arrayContextType }, fieldProp.Resolve, idLambda);

            body = ExpressionUtil.MakeExpressionCall(new[] { typeof(Queryable), typeof(Enumerable) }, "FirstOrDefault", new Type[] { arrayContextType }, body);
            var contextParam = Expression.Parameter(contextType);
            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}";
            }
            var field = new Field(name, selectionExpression, $"Return a {fieldProp.ReturnTypeClrSingle} by its Id", fieldProp.ReturnTypeClrSingle, argTypesValue);

            schema.AddField(field);
        }
Esempio n. 3
0
        private static ISchemaType CacheType <TContextType>(Type propType, MappedSchemaProvider <TContextType> schema)
        {
            if (propType.IsEnumerableOrArray())
            {
                propType = propType.GetEnumerableOrArrayType();
            }

            if (!schema.HasType(propType.Name) && !ignoreTypes.Contains(propType.Name))
            {
                var    typeInfo    = propType.GetTypeInfo();
                string description = "";
                var    d           = (DescriptionAttribute)typeInfo.GetCustomAttribute(typeof(DescriptionAttribute), false);
                if (d != null)
                {
                    description = d.Description;
                }

                if (typeInfo.IsClass || typeInfo.IsInterface)
                {
                    // add type before we recurse more that may also add the type
                    // dynamcially call generic method
                    // hate this, but want to build the types with the right Genenics so you can extend them later.
                    // this is not the fastest, but only done on schema creation
                    var method = schema.GetType().GetMethod("AddType", new [] { typeof(string), typeof(string) });
                    method = method.MakeGenericMethod(propType);
                    var t = (ISchemaType)method.Invoke(schema, new object[] { propType.Name, description });

                    var fields = AddFieldsFromObjectToSchema <TContextType>(propType, schema);
                    t.AddFields(fields);
                    return(t);
                }
                else if (typeInfo.IsEnum && !schema.HasType(propType.Name))
                {
                    var t = schema.AddEnum(propType.Name, propType, description);
                    return(t);
                }
                else if (propType.IsNullableType() && Nullable.GetUnderlyingType(propType).GetTypeInfo().IsEnum&& !schema.HasType(Nullable.GetUnderlyingType(propType).Name))
                {
                    Type type = Nullable.GetUnderlyingType(propType);
                    var  t    = schema.AddEnum(type.Name, type, description);
                    return(t);
                }
            }
            else if (schema.HasType(propType.Name))
            {
                return(schema.Type(propType.Name));
            }
            return(null);
        }
Esempio n. 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, 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 MappedSchemaProvider <TContextType> FromObject <TContextType>(bool autoCreateIdArguments = true, bool authCreateEnumTypes = true)
        {
            var schema      = new MappedSchemaProvider <TContextType>();
            var contextType = typeof(TContextType);
            var rootFields  = AddFieldsFromObjectToSchema <TContextType>(contextType, schema);

            foreach (var f in rootFields)
            {
                if (autoCreateIdArguments)
                {
                    // add non-pural field with argument of ID
                    AddFieldWithIdArgumentIfExists(schema, contextType, f);
                }
                schema.AddField(f);
            }
            return(schema);
        }
Esempio n. 5
0
        private static void AddFieldWithIdArgumentIfExists <TContextType>(MappedSchemaProvider <TContextType> schema, Type contextType, Field fieldProp)
        {
            if (!fieldProp.Resolve.Type.IsEnumerable())
            {
                return;
            }
            var schemaType = schema.Type(fieldProp.ReturnSchemaType);
            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);
            Type       arrayContextType  = schema.Type(fieldProp.ReturnSchemaType).ContextType;
            var        arrayContextParam = Expression.Parameter(arrayContextType);
            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.MakeExpressionCall(new[] { typeof(Queryable), typeof(Enumerable) }, "Where", new Type[] { arrayContextType }, fieldProp.Resolve, idLambda);

            body = ExpressionUtil.MakeExpressionCall(new[] { typeof(Queryable), typeof(Enumerable) }, "FirstOrDefault", new Type[] { arrayContextType }, body);
            var contextParam = Expression.Parameter(contextType);
            var lambdaParams = new[] { contextParam, argTypeParam };

            body = new ParameterReplacer().ReplaceByType(body, contextType, contextParam);
            var selectionExpression = Expression.Lambda(body, lambdaParams);
            var field = new Field(fieldProp.Name.Singularize(), selectionExpression, $"Return {fieldProp.ReturnSchemaType} by Id", fieldProp.ReturnSchemaType, argTypesValue);

            schema.AddField(field);
        }
Esempio n. 6
0
        public ISchemaType AddAllFields <TContextType>(MappedSchemaProvider <TContextType> schema, bool autoCreateNewComplexTypes = false, bool autoCreateEnumTypes = true)
        {
            if (IsEnum)
            {
                foreach (var field in ContextType.GetTypeInfo().GetFields())
                {
                    if (field.Name == "value__")
                    {
                        continue;
                    }

                    var enumName    = Enum.Parse(ContextType, field.Name).ToString();
                    var description = (field.GetCustomAttribute(typeof(DescriptionAttribute)) as DescriptionAttribute)?.Description;
                    AddField(new Field(enumName, null, description, Name, ContextType));
                }
            }
            else
            {
                var fields = SchemaBuilder.GetFieldsFromObject(ContextType, schema, autoCreateEnumTypes, autoCreateNewComplexTypes);
                AddFields(fields);
            }
            return(this);
        }
Esempio n. 7
0
        private static void CacheType <TContextType>(Type propType, MappedSchemaProvider <TContextType> schema)
        {
            if (propType.GetTypeInfo().IsGenericType&& propType.IsEnumerable())
            {
                propType = propType.GetGenericArguments()[0];
            }

            if (!schema.HasType(propType.Name) && propType.Name != "String" && (propType.GetTypeInfo().IsClass || propType.GetTypeInfo().IsInterface))
            {
                // add type before we recurse more that may also add the type
                // dynamcially call generic method
                var parameters = new List <Expression> {
                    Expression.Constant(propType.Name), Expression.Constant(""), Expression.Constant(null)
                };
                // hate this, but want to build the types with the right Genenics so you can extend them later.
                // this is not the fastest, but only done on schema creation
                var method = schema.GetType().GetMethod("AddType", new [] { typeof(string), typeof(string) });
                method = method.MakeGenericMethod(propType);
                var t = (ISchemaType)method.Invoke(schema, new object[] { propType.Name, propType.Name + " description" });

                var fields = AddFieldsFromObjectToSchema <TContextType>(propType, schema);
                t.AddFields(fields);
            }
        }
Esempio n. 8
0
        private static Field ProcessFieldOrProperty <TContextType>(MemberInfo prop, Type fieldOrPropType, ParameterExpression param, MappedSchemaProvider <TContextType> schema)
        {
            if (ignoreProps.Contains(prop.Name) || GraphQLIgnoreAttribute.ShouldIgnoreMemberFromQuery(prop))
            {
                return(null);
            }

            // Get Description from ComponentModel.DescriptionAttribute
            string description = "";
            var    d           = (DescriptionAttribute)prop.GetCustomAttribute(typeof(DescriptionAttribute), false);

            if (d != null)
            {
                description = d.Description;
            }

            LambdaExpression le = Expression.Lambda(prop.MemberType == MemberTypes.Property ? Expression.Property(param, prop.Name) : Expression.Field(param, prop.Name), param);
            var f = new Field(SchemaGenerator.ToCamelCaseStartsLower(prop.Name), le, description);
            var t = CacheType <TContextType>(fieldOrPropType, schema);

            if (t != null && t.IsEnum && !f.ReturnTypeClr.IsNullableType())
            {
                f.ReturnTypeNotNullable = true;
            }
            return(f);
        }
Esempio n. 9
0
        private static List <Field> AddFieldsFromObjectToSchema <TContextType>(Type type, MappedSchemaProvider <TContextType> schema)
        {
            var fields = new List <Field>();
            // cache fields/properties
            var param = Expression.Parameter(type);

            if (type.IsArray || type.IsEnumerableOrArray())
            {
                return(fields);
            }

            foreach (var prop in type.GetProperties())
            {
                var f = ProcessFieldOrProperty <TContextType>(prop, prop.PropertyType, param, schema);
                if (f != null)
                {
                    fields.Add(f);
                }
            }
            foreach (var prop in type.GetFields())
            {
                var f = ProcessFieldOrProperty <TContextType>(prop, prop.FieldType, param, schema);
                if (f != null)
                {
                    fields.Add(f);
                }
            }
            return(fields);
        }
Esempio n. 10
0
        private static List <Field> AddFieldsFromObjectToSchema <TContextType>(Type type, MappedSchemaProvider <TContextType> schema)
        {
            var fields = new List <Field>();
            // cache fields/properties
            var param = Expression.Parameter(type);

            if (type.IsArray || type.IsEnumerable())
            {
                return(fields);
            }

            foreach (var prop in type.GetProperties())
            {
                LambdaExpression le = Expression.Lambda(Expression.Property(param, prop.Name), param);
                var f = new Field(prop.Name, le, prop.Name);
                fields.Add(f);
                CacheType <TContextType>(prop.PropertyType, schema);
            }
            foreach (var prop in type.GetFields())
            {
                LambdaExpression le = Expression.Lambda(Expression.Field(param, prop.Name), param);
                var f = new Field(prop.Name, le, prop.Name);
                fields.Add(f);
                CacheType <TContextType>(prop.FieldType, schema);
            }
            return(fields);
        }