Skip to content

Honor a property's JsonValueReaderWriter when patching a single JSON property - #39529

Closed
jeremy-morren wants to merge 1 commit into
dotnet:mainfrom
jeremy-morren:json-readerwriter-partial-update
Closed

jeremy-morren wants to merge 1 commit into
dotnet:mainfrom
jeremy-morren:json-readerwriter-partial-update

Conversation

@jeremy-morren

Copy link
Copy Markdown
Contributor

Honor a property's JsonValueReaderWriter when patching a single JSON property

Fixes #39526

When SaveChanges patches a single property in a JSON column, it ignored a JsonValueReaderWriter configured 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 a bit or decimal parameter on SQL Server, whose JSON_MODIFY can'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 setting Code to "D":

-- SQLite before: @p0='D'      stored {"Code":"D"}, reading back fails
-- SQLite after:  @p0='#D'     stored {"Code":"#D"}
UPDATE "Widget" SET "Tag" = json_set("Tag", '$.Code', @p0)

-- SQL Server before: @p0='{"":"D"}'
-- SQL Server after:  @p0='{"":"#D"}'
UPDATE [Widget] SET [Tag] = JSON_MODIFY([Tag], 'strict $.Code', JSON_VALUE(@p0, '$.""'))

@jeremy-morren
jeremy-morren requested a review from a team as a code owner November 4, 2026 20:07
Copilot AI balanced review requested due to automatic review settings November 4, 2026 20:07

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 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.
@jeremy-morren
jeremy-morren force-pushed the json-readerwriter-partial-update branch from 61828af to e0a0544 Compare November 4, 2026 20:49
Copilot AI balanced review requested due to automatic review settings November 4, 2026 20:49

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 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

Comment on lines +129 to +130
JsonValueKind.Number when element.TryGetDouble(out var number)
&& IsSameNumber(element, number.ToString("E15", CultureInfo.InvariantCulture)) => parameters with
@AndriySvyryd

Copy link
Copy Markdown
Member

I think this is already covered by #39483

@jeremy-morren

Copy link
Copy Markdown
Contributor Author

Sorry didn't see that one feel free to close this pr.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Updating a single JSON property ignores its JsonValueReaderWriter

3 participants