Skip to content

Fix projection of required complex type via left join#37928

Open
roji wants to merge 1 commit intodotnet:release/10.0from
roji:RequiredComplexViaLeftJoin
Open

Fix projection of required complex type via left join#37928
roji wants to merge 1 commit intodotnet:release/10.0from
roji:RequiredComplexViaLeftJoin

Conversation

@roji
Copy link
Member

@roji roji commented Mar 15, 2026

Fixes #37304

Description

PR #37196 fixed #37162 by adding an IsNullable flag to StructuralTypeMaterializerSourceParameters to correctly distinguish optional vs required complex types during materialization. However, in StructuralTypeMaterializerSource.AddInitializeExpression, the IsNullable for nested complex properties was computed as nullable || complexProperty.IsNullable, where nullable is the parent entity/type's nullability. When an entity is loaded via a LEFT JOIN (e.g. Include on a nullable navigation), its IsNullable is true, and this incorrectly cascades down to all its required complex types, causing HandleNullableComplexTypeMaterialization to check their scalar properties and return null when all are null.

The fix removes the nullable || from the expression, using only complexProperty.IsNullable to determine the complex property's nullability. The parent's nullability is already handled at the entity level (discriminator/key check); once the entity is confirmed to exist, its required complex types must always be materialized as non-null.

Customer impact

When loading a non-optional complex type (with all-nullable properties) on an entity accessed via a LEFT JOIN — such as through Include on a collection navigation or a nullable reference navigation — EF Core 10 incorrectly returns null for the complex type instead of a non-null instance with null properties. This is a regression from EF 8/9 and violates the user's non-nullable configuration.

How found

Multiple customers reported on 10.0 (#37304)

Regression

Yes — introduced by the fix for #37162 (PR #37196) in 10.0.1.

Testing

Added regression test Non_optional_complex_type_with_all_nullable_properties_via_left_join in AdHocComplexTypeQueryTestBase.cs.

Risk

Very low. Single-character change (nullable || complexProperty.IsNullable → complexProperty.IsNullable). The parent entity's nullability was never needed here — entity-level null checks already prevent complex type materialization from being reached when the entity doesn't exist. Quirk added just in case.

@roji roji requested a review from a team as a code owner March 15, 2026 07:11
Copilot AI review requested due to automatic review settings March 15, 2026 07:11
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes a regression in EF Core 10 complex-type materialization when entities are produced via LEFT JOINs (e.g., Include), ensuring required complex properties are still materialized even when all their scalar columns are NULL.

Changes:

  • Adjust complex-property nullability propagation during materialization so required complex properties don’t inherit the parent structural type’s “nullable due to LEFT JOIN” state.
  • Add an AppContext switch (Microsoft.EntityFrameworkCore.Issue37304) to allow reverting to the previous behavior if needed.
  • Add a regression test covering required complex type materialization via a LEFT JOIN include.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
test/EFCore.Specification.Tests/Query/AdHocComplexTypeQueryTestBase.cs Adds a regression test asserting a required complex type is materialized (non-null) via LEFT JOIN include even when all its properties are null.
src/EFCore/Query/Internal/StructuralTypeMaterializerSource.cs Stops cascading parent nullability into nested complex-property materialization (with an opt-out AppContext switch).

You can also share your feedback on Copilot code review. Take the survey.

@roji
Copy link
Member Author

roji commented Mar 16, 2026

/azp run

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants