Skip to content

6.2.4

Latest

Choose a tag to compare

@lukemurray lukemurray released this 08 Sep 17:45
dd0d510

Fixes

  • Fixed No generic method 'SelectWithNullCheck' for a sub-selection on a field returning IAsyncEnumerable<T> or ValueTask<TCollection> - { people { tags { name } } } where tags has a ResolveAsync returning either. MakeSelectWithDynamicType emits a SelectWithNullCheck whose overload is resolved by the exact type of the expression it projects, and only IEnumerable<T> and Task<IEnumerable<T>> had one. IAsyncEnumerable<T> now has an overload of its own that projects lazily, so the stream is still buffered by the engine with the request's CancellationToken rather than being enumerated during compilation, and ValueTask<T> is handed on as the Task<T> the rest of the pipeline already handles, next to the existing Task<TCollection> normalization.
  • Fixed Object of type 'Dynamic_...' cannot be converted to type 'Dynamic_...' when buffering an IAsyncEnumerable<T> whose items are rebuilt - the list rebuild mismatch below, one layer down. BufferAsyncEnumerable created a List<T> from the declared element type before resolving anything and added each resolved item to it, so an item rebuilt to carry an awaited member no longer fit. Items are now resolved first and the list type chosen from them, which is what the IEnumerable path already did - both paths now share that step. The same guard added above for Task<T> and ValueTask<T> in GetResolvedFieldType applies to IAsyncEnumerable<T>, which is reachable now that these shapes compile.
  • Fixed Object of type 'System.Collections.Generic.List1[System.Object]' cannot be converted to type 'System.Collections.Generic.IEnumerable1[Dynamic_...]' for a query selecting an async service field below an async service list field - { people { tags { name label } } } where tags has a ResolveAsync returning a list and label on the item type has one of its own. Resolving an item of the outer list rebuilds it, because the item projection holds an async member, so the finished list no longer holds the item type the outer field was declared with and correctly falls back to List<object>. GetResolvedFieldType unwrapped Task<T> to T unconditionally, so the rebuilt parent still declared the member IEnumerable<T> and setting the resolved list on it threw. T is now only kept when the resolved value still is one, otherwise the resolved value's own type is used - which is what the non-async path in the same method already did. ValueTask<T> had the same hole and takes the same guard.