[release/10.0] Fix struct complex property nullability when reading EF Core 8.x/9.x model snapshots#37690
Conversation
Co-authored-by: AndriySvyryd <6539701+AndriySvyryd@users.noreply.github.com>
Co-authored-by: AndriySvyryd <6539701+AndriySvyryd@users.noreply.github.com>
Co-authored-by: AndriySvyryd <6539701+AndriySvyryd@users.noreply.github.com>
src/EFCore.Design/Migrations/Internal/SnapshotModelProcessor.cs
Outdated
Show resolved
Hide resolved
src/EFCore.Design/Migrations/Internal/SnapshotModelProcessor.cs
Outdated
Show resolved
Hide resolved
…hanges - Only check for versions 8 and 9 (inline version check) - Remove cast to ComplexProperty and use IMutableComplexProperty directly - Revert all changes to Migrator.cs (runtime processing not needed) Co-authored-by: AndriySvyryd <6539701+AndriySvyryd@users.noreply.github.com>
|
@copilot Update progress by rewriting the PR summary using this template: |
... Updated the PR description with the required template format. |
There was a problem hiding this comment.
Pull request overview
This PR fixes a regression introduced in EF Core 10.0 that causes false positive PendingModelChangesWarning errors for applications upgrading from EF Core 8.x or 9.x that use struct-type complex properties. The issue occurs because EF Core 10.0 added support for optional complex properties, and when reading snapshots generated by earlier versions (which don't have explicit .IsRequired() calls), EF Core 10.0 incorrectly infers struct complex properties as nullable.
The fix adds processing in SnapshotModelProcessor to explicitly mark struct complex properties as required when loading model snapshots with version "8.x" or "9.x". This ensures that legacy snapshots are correctly interpreted during migration operations. The fix is activated when customers run dotnet ef migrations add, which regenerates the snapshot file with explicit .IsRequired() calls, resolving both design-time and runtime issues.
Changes:
- Added
ProcessComplexPropertiesmethod to recursively process complex properties and nested complex properties in entity types - Added
UpdateComplexPropertyNullabilitymethod to setIsNullable = falsefor struct complex properties in 8.x/9.x snapshots - Added comprehensive unit tests covering standard and nested complex properties for both pre-10.0 and 10.0+ snapshots
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| src/EFCore.Design/Migrations/Internal/SnapshotModelProcessor.cs | Added methods to process complex properties and mark struct complex properties as required for 8.x/9.x model snapshots |
| test/EFCore.Design.Tests/Migrations/Design/SnapshotModelProcessorTest.cs | Added three test methods covering struct complex property nullability processing for different version scenarios and nested complex properties |
test/EFCore.Design.Tests/Migrations/Design/SnapshotModelProcessorTest.cs
Show resolved
Hide resolved
artl93
left a comment
There was a problem hiding this comment.
Approved. Regression in migrating across versions, customer reported.
Fixes #37683
Description
After upgrading from EF Core 9.0 to 10.0, applications with struct-type complex properties receive false
PendingModelChangesWarningerrors during migration, even when no model changes were made.EF Core 9.0 generates model snapshots without explicit
.IsRequired()calls for struct complex properties because value types are inherently non-nullable in 9.0 (which didn't support optional complex properties). However, EF Core 10.0 added optional complex property support and when reading 9.0 snapshots without explicit.IsRequired(), incorrectly infers struct complex properties as nullable, causing false pending model change warnings.Customer impact
The issue affects all customers upgrading from EF Core 8.x or 9.x to 10.0 who use struct complex properties.
When customers upgrade their application from EF Core 9.x to 10.0:
MigrateAsync()throwsPendingModelChangesWarningexception during application startupdotnet ef migrations addgenerates spurious migration withAlterColumnoperations marking columns as nullablePendingModelChangesWarningis configured as an errorWorkaround: Customers can suppress the warning by configuring
ConfigureWarningsto ignoreRelationalEventId.PendingModelChangesWarning, or generate an empty migration that incorrectly marks the column as having changed from nullable to non-nullable.How found
Customer reported on EF 10.0.3
Regression
Yes, introduced in EF Core 10.0.0 when optional complex property support was added
Testing
Added unit tests
Risk
Low - The fix is narrowly scoped to design-time migration generation from snapshots with version "8.x" or "9.x"
Original prompt
This section details on the original issue you should resolve
<issue_title>ComplexProperty treated as nullable after update from EF Core 9.0 to 10.0</issue_title>
<issue_description>### Bug description
Initial migrations created with EF Core 9.0.13. After updating to 10.0.3 with no other code changes, EF Core incorrectly detects a model change for (struct-type) ComplexProperty columns.
Full demo project at https://github.com/markwijnen/EfCorePendingModelChangesIssue
Demo db context:
Initial migration (with Microsoft.EntityFrameworkCore.Sqlite & Microsoft.EntityFrameworkCore.Design 9.0.13 and
dotnet ef migrations add EfCore09):After updating to Microsoft.EntityFrameworkCore.Sqlite & Microsoft.EntityFrameworkCore.Design 10.0.3,
MigrateAsyncfails withPendingModelChangesWarning, see stack trace below.EF Core seems to detect model changes for the "Value_Value" column, even though there aren't any.
Running the migration tool again (
dotnet ef migrations add EfCore10) adds the following:It looks like EF Core thinks that the "Value_Value" column was nullable in the original schema (
oldNullable: trueinUpmethod andnullable: trueinDownmethod) even though it wasn't nullable in the initial migration (nullable: false).Detected in production with Npgsql.EntityFrameworkCore.PostgreSQL. The demo shows the same error occurring with Microsoft.EntityFrameworkCore.Sqlite.
Possibly related to #36494, but this still occurs on 10.0.3.
Your code
Stack traces