graphql-platform
graphql-platform copied to clipboard
Projection on UnionTypes leads to an Unable to cast Exception
Product
Hot Chocolate
Version
13.8.1
Link to minimal reproduction
https://github.com/phebing/HC-UnionType-Projection-Bug
Steps to reproduce
Create a backend Query like
[ExtendObjectType(OperationTypeNames.Query)]
public class Queries
{
public class Base
{
public string C { get; set; }
}
public class ChildA : Base
{
public string A { get; set; }
}
public class ChildB : Base
{
public string B { get; set; }
}
public class ChildAType : ObjectType<ChildA>
{
protected override void Configure(IObjectTypeDescriptor<ChildA> descriptor)
{
descriptor.Field(_ => _.A).Type<NonNullType<StringType>>();
}
}
public class ChildBType : ObjectType<ChildB>
{
protected override void Configure(IObjectTypeDescriptor<ChildB> descriptor)
{
descriptor.Field(_ => _.B).Type<NonNullType<StringType>>();
}
}
public class UnionTestType : UnionType
{
protected override void Configure(IUnionTypeDescriptor descriptor)
{
base.Configure(descriptor);
descriptor.Name("UnionTestType");
descriptor.Type<ChildAType>();
descriptor.Type<ChildBType>();
}
}
[UseProjection]
[GraphQLType(typeof(ListType<UnionTestType>))]
public IQueryable<Base> GetUnionTest()
{
var types = new List<Base>();
return types.AsQueryable();
}
}
And run a query on it like
query {
unionTest {
__typename
...on ChildA{
a
}
... on ChildB {
b
}
}
}
What is expected?
The Query returns the specific Elements and uses a projected DB call to do so.
What is actually happening?
If you have a Query like GetUnionTest
that returns an UnionType and you add [UseProjection]
then it throws an "Unexpected Execution Error"
with the message
"message": "Unable to cast object of type 'System.Linq.Expressions.Expression1`1[System.Func`2[System.Object,System.Object]]' to type 'System.Linq.Expressions.Expression`1[System.Func`2[omittedNamespace...Queries+Base,omittedNamespace...Queries+Base]]'.",
"stackTrace": " at HotChocolate.Data.Projections.Expressions.QueryableProjectionScopeExtensions.Project[T](QueryableProjectionScope scope)\n at HotChocolate.Data.Projections.Expressions.QueryableProjectionContextExtensions.Project[T](QueryableProjectionContext context)\n at HotChocolate.Data.Projections.Expressions.QueryableProjectionProvider.<CreateApplicator>b__7_0[TEntityType](IResolverContext context, Object input)\n at HotChocolate.Data.Projections.Expressions.QueryableProjectionProvider.<>c__DisplayClass4_0`1.<<CreateExecutor>g__ExecuteAsync|1>d.MoveNext()\n--- End of stack trace from previous location ---\n at HotChocolate.Execution.Processing.Tasks.ResolverTask.ExecuteResolverPipelineAsync(CancellationToken cancellationToken)\n at HotChocolate.Execution.Processing.Tasks.ResolverTask.TryExecuteAsync(CancellationToken cancellationToken)"
Even a query just for the types throws this error
query {
unionTest {
__typename
}
}
In the minimal example I skipped the DB, since the error is also happening on a List.AsQueryable().
The Documentation of Projections is not mentioning if UnionTypes are not supported. So if that is the case, then maybe this should be mentioned.
Relevant log output
[2024-03-01 12:39:34 ERR] An error occurred when resolving /unionTest
System.InvalidCastException: Unable to cast object of type 'System.Linq.Expressions.Expression1`1[System.Func`2[System.Object,System.Object]]' to type 'System.Linq.Expressions.Expression`1[System.Func`2[omittedNamespace...Queries+Base]]'.
at HotChocolate.Data.Projections.Expressions.QueryableProjectionScopeExtensions.Project[T](QueryableProjectionScope scope)
at HotChocolate.Data.Projections.Expressions.QueryableProjectionContextExtensions.Project[T](QueryableProjectionContext context)
at HotChocolate.Data.Projections.Expressions.QueryableProjectionProvider.<CreateApplicator>b__7_0[TEntityType](IResolverContext context, Object input)
at HotChocolate.Data.Projections.Expressions.QueryableProjectionProvider.<>c__DisplayClass4_0`1.<<CreateExecutor>g__ExecuteAsync|1>d.MoveNext()
--- End of stack trace from previous location ---
at HotChocolate.Execution.Processing.Tasks.ResolverTask.ExecuteResolverPipelineAsync(CancellationToken cancellationToken)
at HotChocolate.Execution.Processing.Tasks.ResolverTask.TryExecuteAsync(CancellationToken cancellationToken)
### Additional context
_No response_
There seem to be more Issues going on with UnionTypes and Projections (maybe related): If I have the UnionType as a property of a ResponseType and the query uses projection, then the __typename is always only one of the types, even if the actual object uses the other type. Also in such a setup the query has to ask for the same fields in both types or it will result in an error. So with an object ChildA and ChildB a query like
__typename
... on ChildA { c },
... on ChildB { c }
would work, but you would always have __typename == "ChildA" (or B) but not one of each.
And
__typename
... on ChildA { a },
... on ChildB { b }
would throw an error.
And if the property is a List of the UnionType, then querying that also results in errors.
If this should be supported, then I can create an Issue with minimal examples as well, but feels like this is not supported at the moment.