Repository navigation
Honor a property's JsonValueReaderWriter when patching a single JSON property - #39529
jeremy-morren wants to merge 1 commit into
Conversation
There was a problem hiding this comment.
🟡 Changes recommended
Active issues in custom JSON writer handling can break converted-value round trips, lose numeric precision, and bypass null sentinels during single-property updates.
3 open issues
What changed in this PR
This PR addresses #39526 by honoring property-configured JSON reader/writers when SaveChanges patches a single JSON property in SQL Server or SQLite.
Changes:
- Adds shared logic to detect custom JSON representations during single-property updates.
- Adjusts provider parameter handling and SQL generation for strings, numbers, booleans, and JSON fragments.
- Adds round-trip tests and provider SQL baselines for custom writers.
| File | Description |
|---|---|
test/EFCore.SqlServer.FunctionalTests/Update/ComplexCollectionJsonUpdateSqlServerTest.cs |
Adds SQL Server patch baselines. |
test/EFCore.Sqlite.FunctionalTests/Update/ComplexCollectionJsonUpdateSqliteTest.cs |
Adds SQLite patch baselines. |
test/EFCore.Relational.Specification.Tests/Update/ComplexCollectionJsonUpdateTestBase.cs |
Adds custom writers and shared round-trip tests. |
src/EFCore.SqlServer/Update/Internal/SqlServerUpdateSqlGenerator.cs |
Selects patch expressions using parameter mappings. |
src/EFCore.SqlServer/Update/Internal/SqlServerModificationCommand.cs |
Converts writer output into SQL Server patch parameters. |
src/EFCore.Sqlite.Core/Update/Internal/SqliteUpdateSqlGenerator.cs |
Uses fragment handling for serialized booleans. |
src/EFCore.Sqlite.Core/Update/Internal/SqliteModificationCommand.cs |
Coordinates boolean handling with custom writers. |
src/EFCore.Relational/Update/ModificationCommand.cs |
Applies property writers and distinguishes strings from JSON fragments. |
src/EFCore.Relational/Extensions/Internal/RelationalJsonPropertyExtensions.cs |
Adds shared writer-output comparison logic. |
Review effort: Balanced
| /// </remarks> | ||
| public static string? FindJsonFromPropertyReaderWriter(this IProperty property, object? value) | ||
| { | ||
| if (value == null |
…property A single-property JSON patch sent string, bool and numeric values as plain parameters and used the type mapping's writer for other values, ignoring a JsonValueReaderWriter configured on the property. When one is configured (other than the enum reader/writer set by convention), the patched value now comes from it, including null when it handles null writes: a JSON string is sent as a string, other JSON as a JSON fragment (SQLite json()), or on SQL Server as a bit, decimal or float parameter since JSON_MODIFY can't insert raw JSON scalars. A number that neither a decimal nor a float parameter holds exactly throws rather than being silently changed.
61828af to
e0a0544
Compare
There was a problem hiding this comment.
🟡 Changes recommended
Valid JSON numbers such as 1E400 and -1E400 can produce infinite doubles, causing IsSameNumber to throw JsonException instead of the intended property-specific diagnostic; a finiteness check and overflow regression tests are needed.
2 open issues
2 resolved since last review
1 file not reviewed
src/EFCore.SqlServer/Properties/SqlServerStrings.Designer.cs: Generated file
Review effort: Balanced
| JsonValueKind.Number when element.TryGetDouble(out var number) | ||
| && IsSameNumber(element, number.ToString("E15", CultureInfo.InvariantCulture)) => parameters with |
|
I think this is already covered by #39483 |
|
Sorry didn't see that one feel free to close this pr. |

Honor a property's JsonValueReaderWriter when patching a single JSON property
Fixes #39526
When
SaveChangespatches a single property in a JSON column, it ignored aJsonValueReaderWriterconfigured on the property: string, bool and numeric values were sent as plain parameters, and other values went through the type mapping's writer. Now, when the property's writer writes different JSON than the type mapping's writer, the patched value comes from the property's writer. A JSON string is sent as a string. Other JSON is sent as a JSON fragment on SQLite (json()), and as abitordecimalparameter on SQL Server, whoseJSON_MODIFYcan't insert raw JSON scalars. Properties with no writer of their own keep the existing SQL, and so do enums, whose convention-configured writer writes the same JSON as the type mapping.A writer that writes
"#" + value, after settingCodeto"D":