Skip to content

Fix complex type shadow discriminator incorrectly marked as modified on Update#38111

Closed
Copilot wants to merge 3 commits intomainfrom
copilot/fix-regression-discriminator-modification
Closed

Fix complex type shadow discriminator incorrectly marked as modified on Update#38111
Copilot wants to merge 3 commits intomainfrom
copilot/fix-regression-discriminator-modification

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 15, 2026

Fixes #38105

Regression in 10.0.3: DbSet.Update() on entities with nullable complex types having a discriminator throws InvalidOperationException: The property 'X.Discriminator' is defined as read-only after it has been saved, but its value has been modified or marked as modified.

Cause

PR #37394 (fix for #37337) changed SnapshotFactoryFactory.CreateSnapshotExpression to use default values for shadow properties on complex types. This was correct for ShadowValuesFactoryFactory (query materialization path, where complex type shadow property materialization is unsupported per #35613), but applied too broadly to all snapshot factories — including OriginalValuesFactoryFactory, which reads from IInternalEntry and can resolve shadow values.

The original values snapshot stored null for the discriminator instead of the actual value. Update() then detected a diff against the value-generator-assigned value and marked the read-only discriminator as modified.

Fix

  • SnapshotFactoryFactory.cs: Narrowed the default-value fallback to only apply when the factory parameter is not IInternalEntry. Entry-based factories (OriginalValues, Relationship, Sidecar) can read shadow values from the entry's shadow storage directly.
if (!UseOldBehavior37337
    && propertyBase.DeclaringType is IComplexType
    && property.IsShadowProperty()
    && (parameter == null || !parameter.Type.IsAssignableTo(typeof(IInternalEntry))))
  • AdHocComplexTypeQueryTestBase.cs: Added regression test Update_entity_with_nullable_complex_type_and_discriminator — seeds entity with nullable complex type + discriminator, loads it, calls context.Update(), asserts SaveChangesAsync() succeeds.

Existing #37337 test continues to pass (shadow values during query materialization still use defaults).

…on Update

The fix for #37337 (PR #37394) applied too broadly to all snapshot factories,
storing default values for shadow properties on complex types even when reading
from IInternalEntry (which can correctly access shadow values). This caused
OriginalValuesFactoryFactory to store null instead of the actual discriminator
value, making change detection falsely mark the discriminator as modified.

Now the default-value behavior only applies when the snapshot factory parameter
is not IInternalEntry (e.g., during query materialization via ShadowValuesFactoryFactory
where shadow property materialization on complex types is unsupported).

Agent-Logs-Url: https://github.com/dotnet/efcore/sessions/1c384fcd-4abb-47ae-b177-e03ff1a12997

Co-authored-by: AndriySvyryd <6539701+AndriySvyryd@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix regression bug with init-only discriminator modification Fix complex type shadow discriminator incorrectly marked as modified on Update Apr 15, 2026
Copilot AI requested a review from AndriySvyryd April 15, 2026 18:06
@AndriySvyryd AndriySvyryd changed the base branch from release/10.0 to main April 15, 2026 20:16
@AndriySvyryd
Copy link
Copy Markdown
Member

Superseded by #38115

@AndriySvyryd AndriySvyryd deleted the copilot/fix-regression-discriminator-modification branch April 16, 2026 15:16
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.

Complex type init only Discriminator mistakenly marked as modified when unchanged (regression)

2 participants