Beispiel #1
0
        public ExpressionResult GetExpressionForField(Expression context, string typeName, string fieldName, Dictionary <string, ExpressionResult> args, ClaimsIdentity claims)
        {
            if (!types.ContainsKey(typeName))
            {
                throw new EntityQuerySchemaException($"{typeName} not found in schema.");
            }

            var field  = types[typeName].GetField(fieldName, claims);
            var result = new ExpressionResult(field.Resolve ?? Expression.Property(context, fieldName), field.Services);

            if (field.ArgumentTypesObject != null)
            {
                var argType = field.ArgumentTypesObject.GetType();
                // get the values for the argument anonymous type object constructor
                var propVals  = new Dictionary <PropertyInfo, object>();
                var fieldVals = new Dictionary <FieldInfo, object>();
                // if they used AddField("field", new { id = Required<int>() }) the compiler makes properties and a constructor with the values passed in
                foreach (var argField in argType.GetProperties())
                {
                    var val = BuildArgumentFromMember(args, field, argField.Name, argField.PropertyType, argField.GetValue(field.ArgumentTypesObject));
                    // if this was a EntityQueryType we actually get a Func from BuildArgumentFromMember but the anonymous type requires EntityQueryType<>. We marry them here, this allows users to EntityQueryType<> as a Func in LINQ methods while not having it defined until runtime
                    if (argField.PropertyType.IsConstructedGenericType && argField.PropertyType.GetGenericTypeDefinition() == typeof(EntityQueryType <>))
                    {
                        // make sure we create a new instance and not update the schema
                        var entityQuery = Activator.CreateInstance(argField.PropertyType);

                        // set Query
                        var hasValue = val != null;
                        if (hasValue)
                        {
                            var genericProp = entityQuery.GetType().GetProperty("Query");
                            genericProp.SetValue(entityQuery, ((ExpressionResult)val).Expression);
                        }

                        propVals.Add(argField, entityQuery);
                    }
                    else
                    {
                        if (val != null && val.GetType() != argField.PropertyType)
                        {
                            val = ExpressionUtil.ChangeType(val, argField.PropertyType);
                        }
                        propVals.Add(argField, val);
                    }
                }
                // The auto argument is built at runtime from LinqRuntimeTypeBuilder which just makes public fields
                // they could also use a custom class, so we need to look for both fields and properties
                foreach (var argField in argType.GetFields())
                {
                    var val = BuildArgumentFromMember(args, field, argField.Name, argField.FieldType, argField.GetValue(field.ArgumentTypesObject));
                    fieldVals.Add(argField, val);
                }

                // create a copy of the anonymous object. It will have the default values set
                // there is only 1 constructor for the anonymous type that takes all the property values
                var    con = argType.GetConstructor(propVals.Keys.Select(v => v.PropertyType).ToArray());
                object parameters;
                if (con != null)
                {
                    parameters = con.Invoke(propVals.Values.ToArray());
                    foreach (var item in fieldVals)
                    {
                        item.Key.SetValue(parameters, item.Value);
                    }
                }
                else
                {
                    // expect an empty constructor
                    con        = argType.GetConstructor(new Type[0]);
                    parameters = con.Invoke(new object[0]);
                    foreach (var item in fieldVals)
                    {
                        item.Key.SetValue(parameters, item.Value);
                    }
                    foreach (var item in propVals)
                    {
                        item.Key.SetValue(parameters, item.Value);
                    }
                }
                // tell them this expression has another parameter
                var argParam = Expression.Parameter(argType, $"arg_{argType.Name}");
                result.Expression = new ParameterReplacer().ReplaceByType(result.Expression, argType, argParam);
                result.AddConstantParameter(argParam, parameters);
            }

            // the expressions we collect have a different starting parameter. We need to change that
            var paramExp = field.FieldParam;

            result.Expression = new ParameterReplacer().Replace(result.Expression, paramExp, context);

            return(result);
        }
Beispiel #2
0
        public ExpressionResult GetExpressionForField(Expression context, string typeName, string fieldName, Dictionary <string, ExpressionResult> args)
        {
            if (!_types.ContainsKey(typeName))
            {
                throw new EntityQuerySchemaError($"{typeName} not found in schema.");
            }

            var field  = _types[typeName].GetField(fieldName, args != null ? args.Select(f => f.Key).ToArray() : new string[0]);
            var result = new ExpressionResult(field.Resolve ?? Expression.Property(context, fieldName));

            if (field.ArgumentTypes != null)
            {
                var argType = field.ArgumentTypes.GetType();
                // get the values for the argument anonymous type object constructor
                var propVals  = new Dictionary <PropertyInfo, object>();
                var fieldVals = new Dictionary <FieldInfo, object>();
                // if they used AddField("field", new { id = Required<int>() }) the compiler makes properties and a constructor with the values passed in
                foreach (var argField in argType.GetProperties())
                {
                    var val = BuildArgumentFromMember(args, field, argField.Name, argField.PropertyType, argField.GetValue(field.ArgumentTypes));
                    propVals.Add(argField, val);
                }
                // The auto argument is built at runtime from LinqRuntimeTypeBuilder which just makes public fields
                // they could also use a custom class
                foreach (var argField in argType.GetFields())
                {
                    var val = BuildArgumentFromMember(args, field, argField.Name, argField.FieldType, argField.GetValue(field.ArgumentTypes));
                    fieldVals.Add(argField, val);
                }

                // create a copy of the anonymous object. It will have the default values set
                // there is only 1 constructor for the anonymous type that takes all the property values
                var    con = argType.GetConstructor(propVals.Values.Select(v => v.GetType()).ToArray());
                object parameters;
                if (con != null)
                {
                    parameters = con.Invoke(propVals.Values.ToArray());
                    foreach (var item in fieldVals)
                    {
                        item.Key.SetValue(parameters, item.Value);
                    }
                }
                else
                {
                    // expect an empty constructor
                    con        = argType.GetConstructor(new Type[0]);
                    parameters = con.Invoke(new object[0]);
                    foreach (var item in fieldVals)
                    {
                        item.Key.SetValue(parameters, item.Value);
                    }
                    foreach (var item in propVals)
                    {
                        item.Key.SetValue(parameters, item.Value);
                    }
                }
                // tell them this expression has another parameter
                var argParam = Expression.Parameter(argType);
                result.Expression = new ParameterReplacer().ReplaceByType(result.Expression, argType, argParam);
                result.AddConstantParameter(argParam, parameters);
            }

            // the expressions we collect have a different starting parameter. We need to change that
            var paramExp = field.FieldParam;

            result.Expression = new ParameterReplacer().Replace(result.Expression, paramExp, context);

            return(result);
        }