Ejemplo n.º 1
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);
        }
Ejemplo n.º 2
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);
        }
Ejemplo n.º 3
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);
        }