Skip to content

Sort with Variables tries to instantiate an object from the target collection, fails if no parameterless constructor available #6545

@ghost

Description

Is there an existing issue for this?

  • I have searched the existing issues

Product

Hot Chocolate

Describe the bug

I have a business backend that deals, among other entities, with subjects and their addresses. In the database, handled via Entity Framework Core 7, there is a relation between the subject and its addresses, so that inside the Subject entity there is an "Addresses" navigation property which is defined as an ICollection

. Both classes have no parameterless constructor to fill non-nullable property values.

When returning an IQueryable to HotChocolate, I have the query set up like this:

  [UsePaging(IncludeTotalCount = true, MaxPageSize = 100)]
  [UseFiltering(typeof(SubjectFilterType))]
  [UseSorting(typeof(SubjectSortInputType))]
  public IQueryable<ISubject> ListSubjects([Service] IReadRepository<Subject> repo)
    => repo.AsQueryable(new SubjectsNonDeletedSpec());

The readRepository wraps and ultimately returns the DbSet from Entity Framework, just mixing in a .Where condition on the DeletionDate field.

For the purpose of the bug, I don't think filtering middleware is an issue; the sorting middleware is configured with these settings:

  public class SubjectSortInputType : SortInputType<Subject>
  {
    protected override void Configure(ISortInputTypeDescriptor<Subject> descriptor)
    {
      descriptor.BindFieldsImplicitly();

      descriptor
        .Field(f => f.Addresses.FirstOrDefault(address => address.AddressType == AddressType.LegalResidential))
        .Type<AddressSortInputType>()
        .Name("legalResidentialAddress");
    }
  }

In this way, a virtual "legalResidentialAddress" field is added to the GraphQL schema, and it is supposed to be able to be used for sorting.

Indeed when the query is like this:

  query subsorting {
    subject {
      listSubjects(order: { legalResidentialAddress: { cityName: ASC }})
      {
        nodes {
          shorthandDescription
        }
      }
    }
  }

the expected results are received.

But when the query includes variables, like this:

  query subsorting($order: [SubjectSortInput!]) {
    subject {
      listSubjects(order: $order)
      {
        nodes {
          shorthandDescription
        }
      }
    }
  }

  {
    "order": [
      {
        "legalResidentialAddress": {
          "cityName": "ASC"
        }
      }
    ]
  }

An exception is raised and no errors are returned. Exception details and stacktrace is below.

I don't expect the difference between using and not using variables to produce the instantiation of an object from the target class of the sorting collection; why is it happening? Is a parameterless constructor actually needed?

Steps to reproduce

Due to the complexity of producing a sample showcase github repo, it will be added if the above description is not enough to understand the issue clearly.

Relevant log output

[19:32:26 INF] Now listening on: http://localhost:5000
   [19:32:26 INF] Application started. Press Ctrl+C to shut down.
   [19:32:26 INF] Hosting environment: Development
   [19:32:26 INF] Content root path: /workspace/backend/src/Web/bin/Debug/net7.0
   [19:32:32 ERR] Error on GraphQL request
   HotChocolate.GraphQLException: Variable `order` got an invalid value.
      at HotChocolate.Execution.Processing.VariableCoercionHelper.CoerceVariableValue(VariableDefinitionNode variableDefinition, IInputType variableType, Object value)
      at HotChocolate.Execution.Processing.VariableCoercionHelper.CoerceVariableValues(ISchema schema, IReadOnlyList`1 variableDefinitions, IReadOnlyDictionary`2 values, IDictionary`2 coercedValues)
      at HotChocolate.Execution.Pipeline.PipelineTools.CoerceVariables(IRequestContext context, VariableCoercionHelper coercionHelper, IReadOnlyList`1 variableDefinitions)
      at HotChocolate.Execution.Pipeline.OperationVariableCoercionMiddleware.InvokeAsync(IRequestContext context)
      at HotChocolate.Execution.Pipeline.OperationResolverMiddleware.InvokeAsync(IRequestContext context)
      at HotChocolate.Execution.Pipeline.OperationComplexityMiddleware.InvokeAsync(IRequestContext context)
      at HotChocolate.Execution.Pipeline.OperationCacheMiddleware.InvokeAsync(IRequestContext context)
      at HotChocolate.Execution.Pipeline.DocumentValidationMiddleware.InvokeAsync(IRequestContext context)
      at HotChocolate.Execution.Pipeline.DocumentParserMiddleware.InvokeAsync(IRequestContext context)
      at HotChocolate.Execution.Pipeline.DocumentCacheMiddleware.InvokeAsync(IRequestContext context)
      at HotChocolate.Execution.Pipeline.TimeoutMiddleware.InvokeAsync(IRequestContext context)
      at HotChocolate.Execution.Pipeline.ExceptionMiddleware.InvokeAsync(IRequestContext context)
   [19:32:32 ERR] Single error details: HC0016 Variable `order` got an invalid value. at path null
   System.MissingMethodException: Cannot dynamically create an instance of type 'XYZ.Core.Anag.SubjectAggregate.Address'. Reason: No parameterless constructor defined.
      at System.RuntimeType.ActivatorCache..ctor(RuntimeType rt)
      at System.RuntimeType.CreateInstanceDefaultCtor(Boolean publicOnly, Boolean wrapExceptions)
      at HotChocolate.Utilities.DictionaryToObjectConverter.VisitObject(IReadOnlyDictionary`2 dictionary, ConverterContext context)
      at HotChocolate.Utilities.DictionaryVisitor`1.Visit(Object value, TContext context)
      at HotChocolate.Utilities.DictionaryToObjectConverter.Convert(Object from, Type to)
      at HotChocolate.Types.InputParser.ConvertValue(Type requestedType, Object value)
      at HotChocolate.Types.InputParser.ParseObject(IValueNode resultValue, InputObjectType type, Path path, Int32 stack, Boolean defaults)
      at HotChocolate.Types.InputParser.ParseLiteralInternal(IValueNode value, IType type, Path path, Int32 stack, Boolean defaults, IInputFieldInfo field)
      at HotChocolate.Types.InputParser.ParseLiteralInternal(IValueNode value, IType type, Path path, Int32 stack, Boolean defaults, IInputFieldInfo field)
      at HotChocolate.Types.InputParser.ParseList(IValueNode resultValue, ListType type, Path path, Int32 stack, Boolean defaults, IInputFieldInfo field)
      at HotChocolate.Types.InputParser.ParseLiteralInternal(IValueNode value, IType type, Path path, Int32 stack, Boolean defaults, IInputFieldInfo field)
      at HotChocolate.Types.InputParser.ParseLiteral(IValueNode value, IType type, Path path)
      at HotChocolate.Execution.Processing.VariableCoercionHelper.CoerceVariableValue(VariableDefinitionNode variableDefinition, IInputType variableType, Object value)

Additional Context?

No response

Version

13.2.1

Metadata

Metadata

Assignees

No one assigned

    Labels

    Area: DataIssue is related to filtering, sorting, pagination or projections🌶️ hot chocolate

    Type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions