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."); } ISchemaType schemaType = types[typeName]; if (!AuthUtil.IsAuthorized(claims, schemaType.AuthorizeClaims)) { throw new EntityGraphQLAccessException($"You do not have access to the '{typeName}' type. You require any of the following security claims [{string.Join(", ", schemaType.AuthorizeClaims.Claims.SelectMany(r => r))}]"); } var field = schemaType.GetField(fieldName, claims); if (!AuthUtil.IsAuthorized(claims, field.ReturnType.SchemaType.AuthorizeClaims)) { throw new EntityGraphQLAccessException($"You do not have access to the '{field.ReturnType.SchemaType.Name}' type. You require any of the following security claims [{string.Join(", ", field.ReturnType.SchemaType.AuthorizeClaims.Claims.SelectMany(r => r))}]"); } 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); }