Skip to content

Commit 39beebf

Browse files
rojiCopilot
andauthored
Fix struct complex type boxing in collection materialization (#37934)
Fixes #37926 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 3b8b5b8 commit 39beebf

4 files changed

Lines changed: 95 additions & 2 deletions

File tree

src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitor.ShaperProcessingExpressionVisitor.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1479,7 +1479,13 @@ Expression CompensateForCollectionMaterialization(ParameterExpression parameter,
14791479
{
14801480
if (_containsCollectionMaterialization)
14811481
{
1482-
_valuesArrayInitializers!.Add(parameter);
1482+
var expressionToAdd = (Expression)parameter;
1483+
if (expressionToAdd.Type.IsValueType)
1484+
{
1485+
expressionToAdd = Convert(expressionToAdd, typeof(object));
1486+
}
1487+
1488+
_valuesArrayInitializers!.Add(expressionToAdd);
14831489
return Convert(
14841490
ArrayIndex(
14851491
_valuesArrayExpression!,

test/EFCore.Cosmos.FunctionalTests/Query/Associations/ComplexProperties/ComplexPropertiesCollectionCosmosTest.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,10 @@ FROM root c
221221
}
222222

223223

224+
// Cosmos doesn't support entity collection navigations across documents
225+
public override Task Project_struct_complex_type_with_entity_collection_navigation()
226+
=> Task.CompletedTask;
227+
224228
[ConditionalFact]
225229
public virtual void Check_all_tests_overridden()
226230
=> TestHelpers.AssertAllMethodsOverridden(GetType());

test/EFCore.Specification.Tests/Query/Associations/ComplexProperties/ComplexPropertiesCollectionTestBase.cs

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,70 @@ namespace Microsoft.EntityFrameworkCore.Query.Associations.ComplexProperties;
55

66
public abstract class ComplexPropertiesCollectionTestBase<TFixture>(TFixture fixture)
77
: AssociationsCollectionTestBase<TFixture>(fixture)
8-
where TFixture : ComplexPropertiesFixtureBase, new();
8+
where TFixture : ComplexPropertiesFixtureBase, new()
9+
{
10+
#region 37926
11+
12+
[ConditionalFact]
13+
public virtual async Task Project_struct_complex_type_with_entity_collection_navigation()
14+
{
15+
var contextFactory = await InitializeNonSharedTest<Context37926>(
16+
seed: async context =>
17+
{
18+
context.Add(new Context37926.Parent
19+
{
20+
Id = 1,
21+
Coords = new Context37926.Coords { X = 1, Y = 2 },
22+
Children = [new() { Id = 1, Name = "Child1" }]
23+
});
24+
await context.SaveChangesAsync();
25+
});
26+
27+
await using var context = contextFactory.CreateDbContext();
28+
29+
var result = await context.Set<Context37926.Parent>()
30+
.OrderBy(p => p.Id)
31+
.Select(p => new { p.Coords, p.Children })
32+
.FirstAsync();
33+
34+
Assert.Equal(1, result.Coords.X);
35+
Assert.Single(result.Children);
36+
}
37+
38+
protected class Context37926(DbContextOptions options) : DbContext(options)
39+
{
40+
protected override void OnModelCreating(ModelBuilder modelBuilder)
41+
{
42+
modelBuilder.Entity<Parent>(b =>
43+
{
44+
b.Property(e => e.Id).ValueGeneratedNever();
45+
b.ComplexProperty(e => e.Coords);
46+
b.HasMany(e => e.Children).WithOne().HasForeignKey(c => c.ParentId);
47+
});
48+
49+
modelBuilder.Entity<Child>().Property(e => e.Id).ValueGeneratedNever();
50+
}
51+
52+
public class Parent
53+
{
54+
public int Id { get; set; }
55+
public Coords Coords { get; set; }
56+
public List<Child> Children { get; set; } = [];
57+
}
58+
59+
public struct Coords
60+
{
61+
public int X { get; set; }
62+
public int Y { get; set; }
63+
}
64+
65+
public class Child
66+
{
67+
public int Id { get; set; }
68+
public required string Name { get; set; }
69+
public int ParentId { get; set; }
70+
}
71+
}
72+
73+
#endregion 37926
74+
}

test/EFCore.SqlServer.FunctionalTests/Query/Associations/ComplexJson/ComplexJsonCollectionSqlServerTest.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,23 @@ FROM [RootEntity] AS [r]
352352
}
353353
}
354354

355+
public override async Task Project_struct_complex_type_with_entity_collection_navigation()
356+
{
357+
await base.Project_struct_complex_type_with_entity_collection_navigation();
358+
359+
AssertSql(
360+
"""
361+
SELECT [p0].[Coords_X], [p0].[Coords_Y], [p0].[Id], [c].[Id], [c].[Name], [c].[ParentId]
362+
FROM (
363+
SELECT TOP(1) [p].[Coords_X], [p].[Coords_Y], [p].[Id]
364+
FROM [Parent] AS [p]
365+
ORDER BY [p].[Id]
366+
) AS [p0]
367+
LEFT JOIN [Child] AS [c] ON [p0].[Id] = [c].[ParentId]
368+
ORDER BY [p0].[Id]
369+
""");
370+
}
371+
355372
[ConditionalFact]
356373
public virtual void Check_all_tests_overridden()
357374
=> TestHelpers.AssertAllMethodsOverridden(GetType());

0 commit comments

Comments
 (0)