ChilliCream / graphql-platform

Welcome to the home of the Hot Chocolate GraphQL server for .NET, the Strawberry Shake GraphQL client for .NET and Banana Cake Pop the awesome Monaco based GraphQL IDE.
https://chillicream.com
MIT License
5.14k stars 732 forks source link

UseProjection for Relay ID of type long doesn't work #5058

Open vojtechkacmarik opened 2 years ago

vojtechkacmarik commented 2 years ago

Is there an existing issue for this?

Describe the bug

I'm using HotChocolate and AutoMapper projections to provide DTOs from EF entities. (EF entity => DTO => GraphQL API object type). The EF entities have own IDs of type long, the same in DTOs

public long Id { get; set; } 

In the API layer I'm using Relay style (IDs) and Node approach as you can see below.

 descriptor
            .ImplementsNode()
            .IdField(t => t.Id)
            .ResolveNode(async (ctx, id) => await ctx.DataLoader<IGatewayByIdDataLoader>().LoadAsync(id, ctx.RequestAborted));

Services registrations is using Projections, AutoMapper and also GlobalObjectIdentification support. The rest of code omitted for brevity.

services
   .AddProjections()
   .AddGlobalObjectIdentification()
   .AddAutoMapper(typeof(Startup));

Also I have [UseProjection] on Query level and projections is done using extension method ProjectTo

return await _gatewayRepository
            .GetAll()
            .ProjectTo<Gateway, GatewayDto>(context)
            .SingleOrDefaultAsync(entity => entity.DeviceIdentification == deviceIdentification, cancellationToken);

But this approach doesn't work. I am getting error 'Property 'Int64 Id' is not defined for type 'System.Int64' Arg_ParamName_Name'. Not sure what is wrong.

Steps to reproduce

  1. Create simpel GraphQL HotChocolate project.
  2. Introduce simple EF entity and simple DTO with long ID
  3. Enable Relay ID style in GraphQL API object using IdField and Node.
  4. Enable projections for query, globally for project in ConfigureServices
  5. Introduce async Query for simple DTO including Id field and call ProjectTo.

Relevant log output

System.ArgumentException
  HResult=0x80070057
  Message=Property 'Int64 Id' is not defined for type 'System.Int64' Arg_ParamName_Name
  Source=System.Linq.Expressions
  StackTrace:
   at System.Linq.Expressions.Expression.Property(Expression expression, PropertyInfo property)
   at HotChocolate.Data.Projections.Expressions.Handlers.QueryableProjectionScalarHandler.TryHandleLeave(QueryableProjectionContext context, ISelection selection, ISelectionVisitorAction& action)
   at HotChocolate.Data.Projections.ProjectionVisitor`1.Leave(ISelection selection, TContext context)
   at HotChocolate.Data.Projections.SelectionVisitor`1.Visit(ISelection selection, TContext context)
   at HotChocolate.Data.Projections.ProjectionVisitor`1.Visit(ISelection selection, TContext context)
   at HotChocolate.Data.Projections.SelectionVisitor`1.VisitObjectType(IOutputField field, ObjectType objectType, SelectionSetNode selectionSet, TContext context)
   at HotChocolate.Data.Projections.Expressions.QueryableProjectionVisitor.VisitObjectType(IOutputField field, ObjectType objectType, SelectionSetNode selectionSet, QueryableProjectionContext context)
   at HotChocolate.Data.Projections.SelectionVisitor`1.VisitChildren(IOutputField field, TContext context)
   at HotChocolate.Data.Projections.SelectionVisitor`1.Visit(IOutputField field, TContext context)
   at HotChocolate.Data.Projections.ProjectionVisitor`1.Visit(IOutputField field, TContext context)
   at HotChocolate.Data.Projections.SelectionVisitor`1.VisitChildren(ISelection selection, TContext context)
   at HotChocolate.Data.Projections.SelectionVisitor`1.Visit(ISelection selection, TContext context)
   at HotChocolate.Data.Projections.ProjectionVisitor`1.Visit(ISelection selection, TContext context)
   at HotChocolate.Data.Projections.SelectionVisitor`1.VisitObjectType(IOutputField field, ObjectType objectType, SelectionSetNode selectionSet, TContext context)
   at HotChocolate.Data.Projections.Expressions.QueryableProjectionVisitor.VisitObjectType(IOutputField field, ObjectType objectType, SelectionSetNode selectionSet, QueryableProjectionContext context)
   at HotChocolate.Data.Projections.SelectionVisitor`1.VisitChildren(IOutputField field, TContext context)
   at HotChocolate.Data.Projections.SelectionVisitor`1.Visit(IOutputField field, TContext context)
   at HotChocolate.Data.Projections.ProjectionVisitor`1.Visit(IOutputField field, TContext context)
   at HotChocolate.Data.Projections.ProjectionVisitor`1.Visit(TContext context)
   at HotChocolate.Data.AutoMapperQueryableExtensions.ProjectTo[TSource,TResult](IQueryable`1 queryable, IResolverContext context)
   at Kirsch.Cloud.GraphQL.Data.DataProviders.GatewayByDeviceIdentificationDataProvider.<GetAsync>d__2.MoveNext() in D:\Repos\kirsch-cloud-backend-app\Sources\Kirsch.Cloud.GraphQL.Data\DataProviders\GatewayByDeviceIdentificationDataProvider.cs:line 25
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()

  This exception was originally thrown at this call stack:
    System.Linq.Expressions.Expression.Property(System.Linq.Expressions.Expression, System.Reflection.PropertyInfo)
    HotChocolate.Data.Projections.Expressions.Handlers.QueryableProjectionScalarHandler.TryHandleLeave(HotChocolate.Data.Projections.Expressions.QueryableProjectionContext, HotChocolate.Execution.Processing.ISelection, out HotChocolate.Data.Projections.ISelectionVisitorAction)
    HotChocolate.Data.Projections.ProjectionVisitor<TContext>.Leave(HotChocolate.Execution.Processing.ISelection, TContext)
    HotChocolate.Data.Projections.SelectionVisitor<TContext>.Visit(HotChocolate.Execution.Processing.ISelection, TContext)
    HotChocolate.Data.Projections.ProjectionVisitor<TContext>.Visit(HotChocolate.Execution.Processing.ISelection, TContext)
    HotChocolate.Data.Projections.SelectionVisitor<TContext>.VisitObjectType(HotChocolate.Types.IOutputField, HotChocolate.Types.ObjectType, HotChocolate.Language.SelectionSetNode, TContext)
    HotChocolate.Data.Projections.Expressions.QueryableProjectionVisitor.VisitObjectType(HotChocolate.Types.IOutputField, HotChocolate.Types.ObjectType, HotChocolate.Language.SelectionSetNode, HotChocolate.Data.Projections.Expressions.QueryableProjectionContext)
    HotChocolate.Data.Projections.SelectionVisitor<TContext>.VisitChildren(HotChocolate.Types.IOutputField, TContext)
    HotChocolate.Data.Projections.SelectionVisitor<TContext>.Visit(HotChocolate.Types.IOutputField, TContext)
    HotChocolate.Data.Projections.ProjectionVisitor<TContext>.Visit(HotChocolate.Types.IOutputField, TContext)
    ...
    [Call Stack Truncated]

Additional Context?

HotChocolate.Data.AutoMapper 12.9.0 AutoMapper.Extensions.Microsoft.DependencyInjection 11.0.0

Product

Hot Chocolate

Version

12.9.0

vojtechkacmarik commented 2 years ago

Currently I'm using MySQL as database engine using Pomelo.EntityFrameworkCore.MySql 6.0.1.

Also all properties are expanded in mapping configuration in Profile class.

CreateMap<Gateway, GatewayDto>()
            .ForAllMembers(opt => opt.ExplicitExpansion());

Please take a look on it and give me a feedback if there is some missing registrations, for example some TypeConverter or Serializer for long IDs (maybe some conversion or database serialization from long to string (IdType). Probably there should be some QueryableProjectionScalarHandler for concrete type conversion (mapping), right?

vojtechkacmarik commented 2 years ago

It looks like a very similar issue like https://github.com/ChilliCream/hotchocolate/issues/5054