Fix DetectChanges throwing when nullable nested complex property with nested collection is set to null#38667
Open
AndriySvyryd with Copilot wants to merge 2 commits into
Open
Fix DetectChanges throwing when nullable nested complex property with nested collection is set to null#38667AndriySvyryd with Copilot wants to merge 2 commits into
AndriySvyryd with Copilot wants to merge 2 commits into
Conversation
… nested collection is set to null Co-authored-by: AndriySvyryd <6539701+AndriySvyryd@users.noreply.github.com>
Copilot
AI
changed the title
[WIP] Fix ComplexCollection + ToJson() throws InvalidOperationException
Fix DetectChanges throwing when nullable nested complex property with nested collection is set to null
Jul 17, 2026
There was a problem hiding this comment.
Pull request overview
Fixes an InvalidOperationException thrown during change detection when a nullable nested complex property (containing a nested collection) is set to null inside a JSON-mapped complex collection (ToJson()), by ensuring existing tracked entries are consulted before validating CLR collection initialization.
Changes:
- Adjust
InternalComplexCollectionEntry.GetEntryto return existing tracked entries before checking whether the underlying CLR collection is null, enabling ordinal reindexing during cleanup. - Add a relational specification test model/seed + test coverage for the nullable complex property + nested collection scenario (2+ items path).
- Add provider-specific SQL assertions for SQLite and SQL Server.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| src/EFCore/ChangeTracking/Internal/InternalEntryBase.InternalComplexCollectionEntry.cs | Fixes GetEntry ordering to avoid throwing during cleanup/reindexing when parent CLR collection is null but tracked entries still exist. |
| test/EFCore.Relational.Specification.Tests/Update/ComplexCollectionJsonUpdateTestBase.cs | Adds new model/seed and a regression test covering nullable complex property nulled out with nested collection having 2+ elements. |
| test/EFCore.Sqlite.FunctionalTests/Update/ComplexCollectionJsonUpdateSqliteTest.cs | Adds SQLite SQL baseline assertion for the new regression test. |
| test/EFCore.SqlServer.FunctionalTests/Update/ComplexCollectionJsonUpdateSqlServerTest.cs | Adds SQL Server SQL baseline assertion for the new regression test. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #38632
Setting a nullable complex property to
nullduring change detection throwsInvalidOperationException: "must be initialized to a non-null value"when that property's type contains a collection with 2+ items, inside aComplexCollectionmapped withToJson().Root cause:
InternalComplexCollectionEntry.GetEntry()checked whether the CLR collection was null before checking if a tracked entry already existed in_entries. DuringRemoveEntry's ordinal reindexing loop, the tracked entries are valid in-memory even though the parent CLR collection is null (because its parent complex property was just nulled out). With 1 item the reindex loop body never runs; with 2+ items it does, triggeringGetEntry→ null check → throw.Changes:
InternalEntryBase.InternalComplexCollectionEntry.cs— InGetEntry, move the_entries/_originalEntrieslookup before the null CLR collection check. Existing tracked entries are returned directly, bypassing the (now-irrelevant) null check.ComplexCollectionJsonUpdateTestBase.cs— New model (EntityWithNullableMeta/ItemWithMeta/OptionalMeta/MetaEntry), seed data, and testSet_nullable_complex_property_with_nested_collection_to_nullthat exercises the 2-item path.ComplexCollectionJsonUpdateSqliteTest.cs/ComplexCollectionJsonUpdateSqlServerTest.cs— Provider-specific overrides with SQL assertions.