Skip to content

Commit 1ff287c

Browse files
authored
Fix projection of required complex type via left join (#37928)
Fixes #37304
1 parent f1817b2 commit 1ff287c

2 files changed

Lines changed: 83 additions & 1 deletion

File tree

src/EFCore/Query/Internal/StructuralTypeMaterializerSource.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ public static readonly MethodInfo PopulateListMethod
3434
private static readonly bool UseOldBehavior37162 =
3535
AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue37162", out var enabled37162) && enabled37162;
3636

37+
private static readonly bool UseOldBehavior37304 =
38+
AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue37304", out var enabled37304) && enabled37304;
39+
3740
/// <summary>
3841
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
3942
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
@@ -207,7 +210,12 @@ IServiceProperty serviceProperty
207210

208211
IComplexProperty complexProperty
209212
=> CreateMaterializeExpression(
210-
new StructuralTypeMaterializerSourceParameters(complexProperty.ComplexType, "complexType", complexProperty.ClrType, nullable || complexProperty.IsNullable, QueryTrackingBehavior: null),
213+
new StructuralTypeMaterializerSourceParameters(
214+
complexProperty.ComplexType,
215+
"complexType",
216+
complexProperty.ClrType,
217+
UseOldBehavior37304 ? nullable || complexProperty.IsNullable : complexProperty.IsNullable,
218+
QueryTrackingBehavior: null),
211219
bindingInfo.MaterializationContextExpression),
212220

213221
_ => throw new UnreachableException()

test/EFCore.Specification.Tests/Query/AdHocComplexTypeQueryTestBase.cs

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,80 @@ public class ComplexTypeWithAllNulls
268268

269269
#endregion 37162
270270

271+
#region 37304
272+
273+
[ConditionalFact]
274+
public virtual async Task Non_optional_complex_type_with_all_nullable_properties_via_left_join()
275+
{
276+
var contextFactory = await InitializeAsync<Context37304>(
277+
seed: context =>
278+
{
279+
context.Add(
280+
new Context37304.Parent
281+
{
282+
Id = 1,
283+
Children =
284+
[
285+
new Context37304.Child
286+
{
287+
Id = 1,
288+
ComplexType = new Context37304.ComplexTypeWithAllNulls()
289+
}
290+
]
291+
});
292+
return context.SaveChangesAsync();
293+
});
294+
295+
await using var context = contextFactory.CreateContext();
296+
297+
var parent = await context.Set<Context37304.Parent>().Include(p => p.Children).SingleAsync();
298+
299+
var child = parent.Children.Single();
300+
Assert.NotNull(child.ComplexType);
301+
Assert.Null(child.ComplexType.NullableString);
302+
Assert.Null(child.ComplexType.NullableDateTime);
303+
}
304+
305+
private class Context37304(DbContextOptions options) : DbContext(options)
306+
{
307+
protected override void OnModelCreating(ModelBuilder modelBuilder)
308+
{
309+
modelBuilder.Entity<Parent>(b =>
310+
{
311+
b.Property(p => p.Id).ValueGeneratedNever();
312+
});
313+
314+
modelBuilder.Entity<Child>(b =>
315+
{
316+
b.Property(c => c.Id).ValueGeneratedNever();
317+
b.HasOne(c => c.Parent).WithMany(p => p.Children).HasForeignKey(c => c.ParentId);
318+
b.ComplexProperty(c => c.ComplexType);
319+
});
320+
}
321+
322+
public class Parent
323+
{
324+
public int Id { get; set; }
325+
public List<Child> Children { get; set; } = [];
326+
}
327+
328+
public class Child
329+
{
330+
public int Id { get; set; }
331+
public int ParentId { get; set; }
332+
public Parent Parent { get; set; } = null!;
333+
public ComplexTypeWithAllNulls ComplexType { get; set; } = null!;
334+
}
335+
336+
public class ComplexTypeWithAllNulls
337+
{
338+
public string? NullableString { get; set; }
339+
public DateTime? NullableDateTime { get; set; }
340+
}
341+
}
342+
343+
#endregion 37304
344+
271345
#region Issue37337
272346

273347
[ConditionalFact]

0 commit comments

Comments
 (0)