From e35557eba0674cbdb5cea4d86fecc2383b4d28f2 Mon Sep 17 00:00:00 2001 From: Shay Rojansky Date: Mon, 16 Mar 2026 13:02:23 +0100 Subject: [PATCH 1/3] Fix struct complex type boxing in collection materialization When projecting a struct complex type alongside a collection navigation, CompensateForCollectionMaterialization added the value-type expression to _valuesArrayInitializers without boxing, causing NewArrayInit(typeof(object), ...) to throw InvalidOperationException. Box value types before adding to the object[] array, matching the existing pattern in the scalar projection path. Fixes #37926 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- ...sitor.ShaperProcessingExpressionVisitor.cs | 8 ++- .../ComplexPropertiesCollectionCosmosTest.cs | 4 ++ .../ComplexPropertiesCollectionTestBase.cs | 62 ++++++++++++++++++- .../ComplexJsonCollectionSqlServerTest.cs | 17 +++++ 4 files changed, 89 insertions(+), 2 deletions(-) diff --git a/src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitor.ShaperProcessingExpressionVisitor.cs b/src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitor.ShaperProcessingExpressionVisitor.cs index 4735489a932..159f03467d7 100644 --- a/src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitor.ShaperProcessingExpressionVisitor.cs +++ b/src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitor.ShaperProcessingExpressionVisitor.cs @@ -1479,7 +1479,13 @@ Expression CompensateForCollectionMaterialization(ParameterExpression parameter, { if (_containsCollectionMaterialization) { - _valuesArrayInitializers!.Add(parameter); + Expression expressionToAdd = parameter; + if (expressionToAdd.Type.IsValueType) + { + expressionToAdd = Convert(expressionToAdd, typeof(object)); + } + + _valuesArrayInitializers!.Add(expressionToAdd); return Convert( ArrayIndex( _valuesArrayExpression!, diff --git a/test/EFCore.Cosmos.FunctionalTests/Query/Associations/ComplexProperties/ComplexPropertiesCollectionCosmosTest.cs b/test/EFCore.Cosmos.FunctionalTests/Query/Associations/ComplexProperties/ComplexPropertiesCollectionCosmosTest.cs index e92b46d76d0..201952672cb 100644 --- a/test/EFCore.Cosmos.FunctionalTests/Query/Associations/ComplexProperties/ComplexPropertiesCollectionCosmosTest.cs +++ b/test/EFCore.Cosmos.FunctionalTests/Query/Associations/ComplexProperties/ComplexPropertiesCollectionCosmosTest.cs @@ -221,6 +221,10 @@ FROM root c } + // Cosmos doesn't support entity collection navigations across documents. + public override Task Project_struct_complex_type_with_entity_collection_navigation() + => Task.CompletedTask; + [ConditionalFact] public virtual void Check_all_tests_overridden() => TestHelpers.AssertAllMethodsOverridden(GetType()); diff --git a/test/EFCore.Specification.Tests/Query/Associations/ComplexProperties/ComplexPropertiesCollectionTestBase.cs b/test/EFCore.Specification.Tests/Query/Associations/ComplexProperties/ComplexPropertiesCollectionTestBase.cs index 2b4a2cb253e..979fe0b3d5b 100644 --- a/test/EFCore.Specification.Tests/Query/Associations/ComplexProperties/ComplexPropertiesCollectionTestBase.cs +++ b/test/EFCore.Specification.Tests/Query/Associations/ComplexProperties/ComplexPropertiesCollectionTestBase.cs @@ -5,4 +5,64 @@ namespace Microsoft.EntityFrameworkCore.Query.Associations.ComplexProperties; public abstract class ComplexPropertiesCollectionTestBase(TFixture fixture) : AssociationsCollectionTestBase(fixture) - where TFixture : ComplexPropertiesFixtureBase, new(); + where TFixture : ComplexPropertiesFixtureBase, new() +{ + #region 37926 + + [ConditionalFact] + public virtual async Task Project_struct_complex_type_with_entity_collection_navigation() + { + var contextFactory = await InitializeNonSharedTest( + seed: async context => + { + context.Add(new Context37926.Parent + { + Coords = new Context37926.Coords { X = 1, Y = 2 }, + Children = [new() { Name = "Child1" }] + }); + await context.SaveChangesAsync(); + }); + + await using var context = contextFactory.CreateDbContext(); + + var result = await context.Set() + .OrderBy(p => p.Id) + .Select(p => new { p.Coords, p.Children }) + .FirstAsync(); + + Assert.Equal(1, result.Coords.X); + Assert.Single(result.Children); + } + + protected class Context37926(DbContextOptions options) : DbContext(options) + { + protected override void OnModelCreating(ModelBuilder modelBuilder) + => modelBuilder.Entity(b => + { + b.ComplexProperty(e => e.Coords); + b.HasMany(e => e.Children).WithOne().HasForeignKey(c => c.ParentId); + }); + + public class Parent + { + public int Id { get; set; } + public Coords Coords { get; set; } + public List Children { get; set; } = []; + } + + public struct Coords + { + public int X { get; set; } + public int Y { get; set; } + } + + public class Child + { + public int Id { get; set; } + public required string Name { get; set; } + public int ParentId { get; set; } + } + } + + #endregion 37926 +} diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/Associations/ComplexJson/ComplexJsonCollectionSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/Associations/ComplexJson/ComplexJsonCollectionSqlServerTest.cs index 23d6d89e192..7e7c4f68595 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/Associations/ComplexJson/ComplexJsonCollectionSqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/Associations/ComplexJson/ComplexJsonCollectionSqlServerTest.cs @@ -352,6 +352,23 @@ FROM [RootEntity] AS [r] } } + public override async Task Project_struct_complex_type_with_entity_collection_navigation() + { + await base.Project_struct_complex_type_with_entity_collection_navigation(); + + AssertSql( + """ +SELECT [p0].[Coords_X], [p0].[Coords_Y], [p0].[Id], [c].[Id], [c].[Name], [c].[ParentId] +FROM ( + SELECT TOP(1) [p].[Coords_X], [p].[Coords_Y], [p].[Id] + FROM [Parent] AS [p] + ORDER BY [p].[Id] +) AS [p0] +LEFT JOIN [Child] AS [c] ON [p0].[Id] = [c].[ParentId] +ORDER BY [p0].[Id] +"""); + } + [ConditionalFact] public virtual void Check_all_tests_overridden() => TestHelpers.AssertAllMethodsOverridden(GetType()); From 24e46fc1629d8f4b5512bd2ad9099efcbf3364ba Mon Sep 17 00:00:00 2001 From: Shay Rojansky Date: Tue, 17 Mar 2026 00:09:47 +0100 Subject: [PATCH 2/3] Address review feedback: use var, explicit key values, proper Cosmos override Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- ...ressionVisitor.ShaperProcessingExpressionVisitor.cs | 2 +- .../ComplexPropertiesCollectionCosmosTest.cs | 9 ++++++--- .../ComplexPropertiesCollectionTestBase.cs | 10 ++++++++-- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitor.ShaperProcessingExpressionVisitor.cs b/src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitor.ShaperProcessingExpressionVisitor.cs index 159f03467d7..cbd03fd935b 100644 --- a/src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitor.ShaperProcessingExpressionVisitor.cs +++ b/src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitor.ShaperProcessingExpressionVisitor.cs @@ -1479,7 +1479,7 @@ Expression CompensateForCollectionMaterialization(ParameterExpression parameter, { if (_containsCollectionMaterialization) { - Expression expressionToAdd = parameter; + var expressionToAdd = (Expression)parameter; if (expressionToAdd.Type.IsValueType) { expressionToAdd = Convert(expressionToAdd, typeof(object)); diff --git a/test/EFCore.Cosmos.FunctionalTests/Query/Associations/ComplexProperties/ComplexPropertiesCollectionCosmosTest.cs b/test/EFCore.Cosmos.FunctionalTests/Query/Associations/ComplexProperties/ComplexPropertiesCollectionCosmosTest.cs index 201952672cb..31409ceb5f7 100644 --- a/test/EFCore.Cosmos.FunctionalTests/Query/Associations/ComplexProperties/ComplexPropertiesCollectionCosmosTest.cs +++ b/test/EFCore.Cosmos.FunctionalTests/Query/Associations/ComplexProperties/ComplexPropertiesCollectionCosmosTest.cs @@ -221,9 +221,12 @@ FROM root c } - // Cosmos doesn't support entity collection navigations across documents. - public override Task Project_struct_complex_type_with_entity_collection_navigation() - => Task.CompletedTask; + public override async Task Project_struct_complex_type_with_entity_collection_navigation() + { + await base.Project_struct_complex_type_with_entity_collection_navigation(); + + AssertSql(); + } [ConditionalFact] public virtual void Check_all_tests_overridden() diff --git a/test/EFCore.Specification.Tests/Query/Associations/ComplexProperties/ComplexPropertiesCollectionTestBase.cs b/test/EFCore.Specification.Tests/Query/Associations/ComplexProperties/ComplexPropertiesCollectionTestBase.cs index 979fe0b3d5b..2f992282095 100644 --- a/test/EFCore.Specification.Tests/Query/Associations/ComplexProperties/ComplexPropertiesCollectionTestBase.cs +++ b/test/EFCore.Specification.Tests/Query/Associations/ComplexProperties/ComplexPropertiesCollectionTestBase.cs @@ -17,8 +17,9 @@ public virtual async Task Project_struct_complex_type_with_entity_collection_nav { context.Add(new Context37926.Parent { + Id = 1, Coords = new Context37926.Coords { X = 1, Y = 2 }, - Children = [new() { Name = "Child1" }] + Children = [new() { Id = 1, Name = "Child1" }] }); await context.SaveChangesAsync(); }); @@ -37,12 +38,17 @@ public virtual async Task Project_struct_complex_type_with_entity_collection_nav protected class Context37926(DbContextOptions options) : DbContext(options) { protected override void OnModelCreating(ModelBuilder modelBuilder) - => modelBuilder.Entity(b => + { + modelBuilder.Entity(b => { + b.Property(e => e.Id).ValueGeneratedNever(); b.ComplexProperty(e => e.Coords); b.HasMany(e => e.Children).WithOne().HasForeignKey(c => c.ParentId); }); + modelBuilder.Entity().Property(e => e.Id).ValueGeneratedNever(); + } + public class Parent { public int Id { get; set; } From 00e9aa2bf1327d069a10c94fb4984b8d66dadbad Mon Sep 17 00:00:00 2001 From: Shay Rojansky Date: Tue, 17 Mar 2026 14:59:44 +0100 Subject: [PATCH 3/3] Skip Cosmos test for cross-document collection navigation Cosmos doesn't support entity collection navigations across separate documents (requires relational LEFT JOIN), so skip the test. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../ComplexPropertiesCollectionCosmosTest.cs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/test/EFCore.Cosmos.FunctionalTests/Query/Associations/ComplexProperties/ComplexPropertiesCollectionCosmosTest.cs b/test/EFCore.Cosmos.FunctionalTests/Query/Associations/ComplexProperties/ComplexPropertiesCollectionCosmosTest.cs index 31409ceb5f7..b75dee268f4 100644 --- a/test/EFCore.Cosmos.FunctionalTests/Query/Associations/ComplexProperties/ComplexPropertiesCollectionCosmosTest.cs +++ b/test/EFCore.Cosmos.FunctionalTests/Query/Associations/ComplexProperties/ComplexPropertiesCollectionCosmosTest.cs @@ -221,12 +221,9 @@ FROM root c } - public override async Task Project_struct_complex_type_with_entity_collection_navigation() - { - await base.Project_struct_complex_type_with_entity_collection_navigation(); - - AssertSql(); - } + // Cosmos doesn't support entity collection navigations across documents + public override Task Project_struct_complex_type_with_entity_collection_navigation() + => Task.CompletedTask; [ConditionalFact] public virtual void Check_all_tests_overridden()