From fb2a792b3bde44f9cb2d0ef293ed648200c766aa Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Wed, 25 Feb 2026 12:12:49 +0000 Subject: [PATCH 1/2] Fix IsProjected optimizer for composite fields --- .../IsProjectedProjectionOptimizer.cs | 7 +- .../Issue6329ReproTests.cs | 71 +++++++++++++++++++ 2 files changed, 77 insertions(+), 1 deletion(-) create mode 100644 src/HotChocolate/Data/test/Data.Projections.SqlServer.Tests/Issue6329ReproTests.cs diff --git a/src/HotChocolate/Data/src/Data/Projections/Optimizers/IsProjectedProjectionOptimizer.cs b/src/HotChocolate/Data/src/Data/Projections/Optimizers/IsProjectedProjectionOptimizer.cs index 36a108fe8a9..ca1d88160ef 100644 --- a/src/HotChocolate/Data/src/Data/Projections/Optimizers/IsProjectedProjectionOptimizer.cs +++ b/src/HotChocolate/Data/src/Data/Projections/Optimizers/IsProjectedProjectionOptimizer.cs @@ -1,5 +1,6 @@ using HotChocolate.Execution.Processing; using HotChocolate.Language; +using HotChocolate.Types; namespace HotChocolate.Data.Projections.Optimizers; @@ -38,13 +39,17 @@ public Selection RewriteSelection( } var field = context.TypeContext.Fields[fieldName]; + var selectionSet = field.Type.NamedType().IsCompositeType() + ? new SelectionSetNode([]) + : null; + var fieldNode = new FieldNode( null, new NameNode(fieldName), new NameNode(alias), [], [], - null); + selectionSet); var nodesPipeline = context.CompileResolverPipeline(field, fieldNode); diff --git a/src/HotChocolate/Data/test/Data.Projections.SqlServer.Tests/Issue6329ReproTests.cs b/src/HotChocolate/Data/test/Data.Projections.SqlServer.Tests/Issue6329ReproTests.cs new file mode 100644 index 00000000000..262f183232d --- /dev/null +++ b/src/HotChocolate/Data/test/Data.Projections.SqlServer.Tests/Issue6329ReproTests.cs @@ -0,0 +1,71 @@ +using HotChocolate.Execution; +using Microsoft.EntityFrameworkCore; + +namespace HotChocolate.Data.Projections; + +public class Issue6329ReproTests +{ + private readonly SchemaCache _cache = new(); + + private readonly BookWithProjectedAuthor[] _books = + [ + new() + { + Id = 1, + Title = "Book", + Author = new AuthorWithProjectedId + { + Id = 10, + Name = "Author" + } + } + ]; + + [Fact] + public async Task IsProjected_SubObject_Can_Be_Omitted_From_Selection() + { + var tester = _cache.CreateSchema( + _books, + onModelCreating: modelBuilder => + modelBuilder.Entity().OwnsOne(x => x.Author), + usePaging: true); + + var result = await tester.ExecuteAsync( + OperationRequestBuilder.New() + .SetDocument( + """ + { + root { + edges { + node { + id + } + } + } + } + """) + .Build()); + + var operationResult = result.ExpectOperationResult(); + Assert.Empty(operationResult.Errors ?? []); + } + + public class BookWithProjectedAuthor + { + public int Id { get; set; } + + public string Title { get; set; } = string.Empty; + + [IsProjected(true)] + public AuthorWithProjectedId Author { get; set; } = new(); + } + + [Owned] + public class AuthorWithProjectedId + { + [IsProjected(true)] + public int Id { get; set; } + + public string Name { get; set; } = string.Empty; + } +} From 0443ac47c32b31639fe93c3b91870ce4ea84cd8a Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Fri, 27 Feb 2026 12:29:32 +0100 Subject: [PATCH 2/2] fix --- .../Optimizers/IsProjectedProjectionOptimizer.cs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/HotChocolate/Data/src/Data/Projections/Optimizers/IsProjectedProjectionOptimizer.cs b/src/HotChocolate/Data/src/Data/Projections/Optimizers/IsProjectedProjectionOptimizer.cs index ca1d88160ef..8a9aceb0583 100644 --- a/src/HotChocolate/Data/src/Data/Projections/Optimizers/IsProjectedProjectionOptimizer.cs +++ b/src/HotChocolate/Data/src/Data/Projections/Optimizers/IsProjectedProjectionOptimizer.cs @@ -39,9 +39,13 @@ public Selection RewriteSelection( } var field = context.TypeContext.Fields[fieldName]; - var selectionSet = field.Type.NamedType().IsCompositeType() - ? new SelectionSetNode([]) - : null; + + // Only inject leaf fields. Forcing internal object projections can create + // invalid EF Core tracking shapes (e.g. owned entities without owner materialization). + if (field.Type.NamedType().IsCompositeType()) + { + continue; + } var fieldNode = new FieldNode( null, @@ -49,7 +53,7 @@ public Selection RewriteSelection( new NameNode(alias), [], [], - selectionSet); + null); var nodesPipeline = context.CompileResolverPipeline(field, fieldNode);