I'm not sure if my problem is related this pending fix or different.
https://github.com/graphql-dotnet/graphql-dotnet/issues/1831
I'm getting this when I try to add a ListGraphType on a Query Resolver:
Output type 'Query' can have fields only of output types: ScalarGraphType, ObjectGraphType, InterfaceGraphType, UnionGraphType or EnumerationGraphType. Field 'foo_list' has an input type.
Parameter name: fieldType
This used to work OK for me in 2.4 (and prior).
@sungam3r If you have a minute for this one - I'd appreciate it. Thanks much!
That's the designed behavior in 3.0. Output graphs and input graphs are different types - the error message is fairly descriptive. Is there some way I can help further?
The error message is saying that your output graph Query has a field foo_list that returns an input graph type. So for example you can return ListGraphType<StringGraphType> for a list of strings, but for complex objects you'd have ListGraphType<FooGraphType>, and FooGraphType needs to inherit ObjectGraphType (or one of the other ones listed there in the error message). It can't inherit InputObjectGraphType.
Graph types of field arguments can inherit from InputObjectGraphType, however. So like you could have an argument fooArg for field foo_list that accepts ListGraphType<FooInputGraphType> where FooInputGraphType : InputObjectGraphType
I would feel pretty sure that your problem relates to the changes in PR #1436 (or in the same vein, anyway)
Maybe the usage has changed, but the reason I was doing this was because I was splitting out query resolver for requests that only returns objects {} and ones that only return lists [{}].
e.g. I'm using a custom ObjectGraphType and then wrapping in ListGraphType for the ListResolver.
AddField(new FieldType
{
Name = friendlyTableName,
ResolvedType = tableType,
.....
});
//Definition of List Query/Return Type
var listType = new ListGraphType(tableType);
AddField(new FieldType
{
Name = $"{friendlyTableName}_list",
ResolvedType = listType,
....
});
Can you provide a sample of what tableType might be? It would seem that tableType is an InputObjectGraphType, which isn't allowed in 3.0.
public class QueryTableType<T> : ObjectGraphType<object> where T : DbContext
var tableType = new QueryTableType<T>(....)
That seems like it should work. We probably need to set up a test and debug from there. Can you make a PR containing a failing test? Or maybe I can work on it this weekend.
the error message is fairly descriptive
Indeed. @OpenSpacesAndPlaces we need a minimal repro to see what happens.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using GraphQL.Resolvers;
using GraphQL.Types;
using Shouldly;
using Xunit;
namespace GraphQL.Tests.Bugs
{
public class Bug1927 : QueryTestBase<Bug1927Schema>
{
[Fact]
public void should_not_throw_error()
{
//TODO
}
}
public sealed class Table : ObjectGraphType<object>
{
public Table()
{
Name = "Table";
FieldType columnField = Field(typeof(StringGraphType), "Name");
columnField.Resolver = new QueryNameFieldResolver();
}
}
public class QueryNameFieldResolver : IFieldResolver
{
public object Resolve(IResolveFieldContext context)
{
var source = context.Source;
if (source == null)
{
return null;
}
Type t = source.GetType();
bool bIsDictionary = source is IDictionary &&
t.IsGenericType &&
t.GetGenericTypeDefinition().IsAssignableFrom(typeof(Dictionary<,>));
var name = char.ToUpperInvariant(context.FieldAst.Name[0]) + context.FieldAst.Name.Substring(1);
var value = bIsDictionary
? GetDictionaryPropValue((IDictionary)source, name)
: GetObjectPropValue(t, source, name);
value = value != null ? value : string.Empty;
return value;
}
private static object GetObjectPropValue(Type t, object src, string propName)
{
PropertyInfo pi = t.GetProperty(propName, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
return pi?.GetValue(src);
}
private static object GetDictionaryPropValue(IDictionary src, string propName)
{
return src.Contains(propName) ? src[propName] : null;
}
}
public sealed class FakeClass
{
public string Name { get; set; }
}
public sealed class QueryFieldResolver : IFieldResolver
{
public object Resolve(IResolveFieldContext rfc)
{
if (rfc.FieldName.Contains("_list"))
{
return (object)new List<FakeClass>()
{
new FakeClass()
{
Name = "List"
}
};
}
else
{
return (dynamic)new FakeClass()
{
Name = "Object"
};
}
}
}
public sealed class QueryType1927 : ObjectGraphType<object>
{
public QueryType1927()
{
QueryFieldResolver fieldResolver = new QueryFieldResolver();
Table tableType = new Table();
AddField(new FieldType
{
Name = "foo",
Type = tableType.GetType(),
ResolvedType = tableType,
Resolver = fieldResolver
});
//Definition of List Query/Return Type
var listType = new ListGraphType(tableType);
AddField(new FieldType
{
Name = $"foo_list",
Type = listType.GetType(),
ResolvedType = listType,
Resolver = fieldResolver
});
}
}
public class Bug1927Schema : Schema
{
public Bug1927Schema()
{
Query = new QueryType1927();
}
}
}
I think that's about as simply as I can get it to show the general issue.
Any questions - let me know.
I ran your example and it works. What should I see?
OK. Call to Schema.Initialize() did the trick.
I didn't add testing suite assertions - so those won't hit.
At least for me - that example would generate the runtime error:
Output type 'Query' can have fields only of output types: ScalarGraphType, ObjectGraphType, InterfaceGraphType, UnionGraphType or EnumerationGraphType. Field 'foo_list' has an input type.
Parameter name: fieldType
I reproduced the error. It is associated with the use of non-generic versions of ListGraphType and NonNullGraphType. For these container types, it is not possible to determine whether the input or output type will be the type at runtime. For now I advise you to use generic versions instead.
In general, this problem can be solved. It will require a design change. @Shane32 We already talked about this. See #1176 and related issues. The point is to split IGraphType/GraphType into separate branches for input and output types. They have a different set of capabilities and limitations.
Also I could make IsInputType / IsOutputType methods returning bool? instead of bool as a "quick fix" but I won't. It is still breaking change so it will be better to rework entire input/output design for 4.0.
@sungam3r If you have two minutes - can you send a small code snippet of what you mean by:
For now I advise you to use generic versions instead.
Thanks to you both for all the help!
Change:
var listType = new ListGraphType(tableType);
to
var listType = new ListGraphType<Table>() { ResolvedType = tableType };
Exactly. Or if you construct your types in runtime than use MakeGenericType.
This one blows as well in 3.3.2 version where in 2.x worked well in the query
var ft = new FieldType();
ft.Type = typeof(string);
ft.ResolvedType = new StringGraphType();
ft.Name = "test";
AddField(ft);
It's both input and output but the input check blows without even checking if it can be used for the output.
Type should point to the type of the graph type, not the data type -- so for example, ft.Type = typeof(StringGraphType); would be correct. So it is correctly throwing an error in 3.x since string is invalid for a input or output type. However, the line is not required at all if ResolvedType is set. So this should work better:
var ft = new FieldType();
ft.ResolvedType = new StringGraphType();
ft.Name = "test";
AddField(ft);
In the original code posted by @OpenSpacesAndPlaces , a valid workaround would be to remove the two lines indicated below:
public QueryType1927()
{
QueryFieldResolver fieldResolver = new QueryFieldResolver();
Table tableType = new Table();
AddField(new FieldType
{
Name = "foo",
//Type = tableType.GetType(), // <--- comment this out
ResolvedType = tableType,
Resolver = fieldResolver
});
//Definition of List Query/Return Type
var listType = new ListGraphType(tableType);
AddField(new FieldType
{
Name = $"foo_list",
//Type = listType.GetType(), // <--- comment this out
ResolvedType = listType,
Resolver = fieldResolver
});
}
(In that case the issue is still a bug.)
Done.
Most helpful comment
Done.