Skip to content

Conversation

francisphn
Copy link

@francisphn francisphn commented Jun 7, 2025

I came across this when trying to upgrade from v13 to v14 and then to v15. We use [UseMutationConvention] in tandem with [UseProjection] to refetch the entity being returned from database:

    [UseMutationConvention(InputTypeName = nameof(BookInput))]
    [UseProjection]
    public async Task<Callsign?> AddBookAsync(
        BookInput input,
        IResolverContext context,
        BookContext bookContext,
        CancellationToken cancellationToken = default
    )
    {
        var book = new Book(input.Name);

        await bookContext.Books.AddAsync(book, cancellationToken);
        await bookContext.SaveChangesAsync(cancellationToken);
        
        var result = await bookContext.Books
            .Where(it => it.ID == book.ID)
            .Project(context)
            .FirstOrDefaultAsync(cancellationToken);

        return result;
    }

We are excited about the new projection engine in v15 but are taking a lift and shift approach first, and I understand the classic projection engine is not considered deprecated in v15.

Debugging reveals that it seems context.Selection has had a rewrite and thus impacting QueryableProjectionProvider.cs which calls context.Selection.Type.UnwrapRuntimeType(). Whereas context.Selection.Type.UnwrapRuntimeType() would return Book in this case in v13, since v14 it has returned System.Object. This ultimately led to an exception being thrown:

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[Bookshop.Book,Bookshop.Book]]'.

 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.QueryableProjectExtensions.ExecuteProject[T](T input, IResolverContext context, Type expectedType)

at HotChocolate.Data.QueryableProjectExtensions.Project[T](IQueryable`1 queryable, IResolverContext context)

I understand it has been a few years since the maintainers have touched this part of HotChocolate. We are short on time but happy to file a bug report with minimal reproduction if that is the expectation before this PR can be considered.

…t instead of type of entity when classic projection is used in conjunction with mutation convention, due to changes introduced to IResolverContext since v14
@CLAassistant
Copy link

CLAassistant commented Jun 7, 2025

CLA assistant check
All committers have signed the CLA.

@michaelstaib michaelstaib added the 🍒 cherry-pick Consider cherry-picking these changes into the previous major version. label Jul 1, 2025
@michaelstaib michaelstaib self-requested a review July 1, 2025 15:40
@michaelstaib michaelstaib added this to the HC-15.1.8 milestone Jul 1, 2025
@michaelstaib michaelstaib requested a review from Copilot July 1, 2025 15:42
Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR fixes a breaking change in the classic projection engine caused by IResolverContext.Selection.Type.UnwrapRuntimeType() returning System.Object in HotChocolate v14+. It replaces the runtime type lookup with the generic type parameter to restore correct projection behavior.

  • Replace context.Selection.Type.UnwrapRuntimeType() with typeof(TEntityType) in QueryableProjectionProvider.
  • Ensures the correct entity type is used for projection scopes in v14+.
Comments suppressed due to low confidence (2)

src/HotChocolate/Data/src/Data/Projections/Expressions/QueryableProjectionProvider.cs:110

  • [nitpick] Consider adding a brief comment explaining why typeof(TEntityType) is used here instead of the original UnwrapRuntimeType() to clarify the workaround for selection type mismatches introduced in v14+.
                typeof(TEntityType),

src/HotChocolate/Data/src/Data/Projections/Expressions/QueryableProjectionProvider.cs:107

  • Add a unit test that covers the classic projection workflow for a mutation followed by a projection (e.g., with [UseMutationConvention] and [UseProjection]) to prevent regressions in future HotChocolate versions.
            var visitorContext = new QueryableProjectionContext(

Copy link
Member

@michaelstaib michaelstaib left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This fix must have a test showing the issue and proving the fix.

@michaelstaib michaelstaib modified the milestones: HC-15.1.8, HC-15.1.9 Jul 25, 2025
@michaelstaib
Copy link
Member

@francisphn can you provide a test for this that would fail if this fix is not done?

@michaelstaib michaelstaib modified the milestones: HC-15.1.9, HC-15.1.10 Aug 27, 2025
@michaelstaib
Copy link
Member

I have moved this to the next patch release.

@michaelstaib michaelstaib modified the milestones: HC-15.1.10, HC-15.1.11 Aug 27, 2025
@michaelstaib michaelstaib marked this pull request as draft August 28, 2025 07:25
@michaelstaib michaelstaib modified the milestones: HC-15.1.11, HC-15.2.0 Oct 8, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🍒 cherry-pick Consider cherry-picking these changes into the previous major version. 🌶️ hot chocolate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants