Implement JSON_SET() for partial JSON updates, fixing passkey rename error#257
Implement JSON_SET() for partial JSON updates, fixing passkey rename error#257renemadsen merged 10 commits intomasterfrom
Conversation
Co-authored-by: renemadsen <76994+renemadsen@users.noreply.github.com>
Co-authored-by: renemadsen <76994+renemadsen@users.noreply.github.com>
Co-authored-by: renemadsen <76994+renemadsen@users.noreply.github.com>
Co-authored-by: renemadsen <76994+renemadsen@users.noreply.github.com>
Co-authored-by: renemadsen <76994+renemadsen@users.noreply.github.com>
|
@copilot Can we fix this problem by implementing JSON_SET for mysql and mariadb taking into account that the version needs to support the JSON_SET otherwise it can throw an error. |
Great suggestion! Yes, we can implement Based on my analysis:
I'll implement this by:
This will provide a proper fix rather than just a workaround. Working on it now...
I've investigated implementing What I added (commit a668cef):
Why it can't be implemented yet:
What's needed:
The documented |
Co-authored-by: renemadsen <76994+renemadsen@users.noreply.github.com>
|
@copilot What will be required for us to do the same as "Npgsql's jsonb_set() implementation relies on overriding AppendUpdateColumnValue() which isn't virtual in our base class" ? Don't implement it, but figure, what needs to be done and what other ramifications would be the result of doing it that way |
Co-authored-by: renemadsen <76994+renemadsen@users.noreply.github.com>
I've created a comprehensive technical analysis document (commit 55a3270): What Would Be Required:
Key Ramifications:
Recommendation: Do NOT implement. The risk and effort don't justify it when the documented workaround works and EF Core may provide proper hooks in the future. The analysis document has complete implementation steps and code examples for reference. |
|
@copilot revisit this issue and check if any new updates have been made in ef core to fix this, so we can handle this situation? |
…error Override AppendUpdateColumnValue() to generate JSON_SET() SQL when EF Core sends partial JSON column updates. This works around EF Core 10 bug #37411 where only the modified property value is sent instead of the complete JSON object. The fix: - Detects partial JSON updates via columnModification.JsonPath - Generates: JSON_SET(column, '$.path', @param) instead of column = @param - Checks server version support (MySQL 5.7.8+, MariaDB 10.2.3+) - Handles null values via reflection (same approach as Npgsql) - Falls back to standard behavior for non-JSON columns Also cleans up debug comments from earlier investigation commits. Agent-Logs-Url: https://github.com/microting/Pomelo.EntityFrameworkCore.MySql/sessions/89c91c07-7461-4a26-9939-972e68c06152 Co-authored-by: renemadsen <76994+renemadsen@users.noreply.github.com>
Agent-Logs-Url: https://github.com/microting/Pomelo.EntityFrameworkCore.MySql/sessions/89c91c07-7461-4a26-9939-972e68c06152 Co-authored-by: renemadsen <76994+renemadsen@users.noreply.github.com>
Good news — I revisited this and discovered my previous analysis was wrong. Implemented the fix in commits cca0eaf and 1bcba6d:
The passkey rename and all other JSON owned entity update scenarios should now work without any workaround. The upstream EF Core bug (#37411) is still open and unfixed. Warning Firewall rules blocked me from connecting to one or more addresses (expand for details)I tried to connect to the following addresses, but was blocked by firewall rules:
If you need me to access, download, or install something from one of these locations, you can either:
|
EF Core 10 has a bug (dotnet/efcore#37411) where updating properties in JSON-mapped owned entities fails with "Invalid JSON text" error. The bug affects all providers — EF Core sends only the modified property value instead of the complete JSON object, causing MySQL to reject it.
Pomelo now fixes this by overriding
AppendUpdateColumnValue()to generateJSON_SET()SQL for partial JSON column updates, using the same approach as Npgsql'sjsonb_set()implementation for PostgreSQL.The Fix
Changes
src/EFCore.MySql/Update/Internal/MySqlUpdateSqlGenerator.cs— OverridesAppendUpdateColumnValue()to detect partial JSON updates viaJsonPathand generateJSON_SET()SQL. Handles null values via reflection (same approach as Npgsql).src/EFCore.MySql/Infrastructure/ServerVersionSupport.cs— AddedJsonSetversion support propertysrc/EFCore.MySql/Infrastructure/MySqlServerVersion.cs— MySQL 5.7.8+ version check for JSON_SETsrc/EFCore.MySql/Infrastructure/MariaDbServerVersion.cs— MariaDB 10.2.3+ version check for JSON_SETsrc/EFCore.MySql/Storage/Internal/MySqlStructuralJsonTypeMapping.cs— Cleaned up debug commentsdocs/known-issues/json-owned-entity-updates.md— Root cause analysis, fix details, and fallback workarounds for older database versionsdocs/PassKey-Support.md— Updated known issues section to reflect fixdocs/known-issues/README.md— Index of known issues with status indicatorsVersion Requirements
JSON_SET()was introduced)InvalidOperationExceptionwith upgrade guidance and alternative workaroundsContext
The earlier analysis incorrectly concluded that
AppendUpdateColumnValue()was not overridable. Upon re-investigation,UpdateSqlGenerator(EF Core base) does have this as aprotected virtualmethod, and sinceUpdateAndSelectSqlGeneratorextends it, Pomelo can override it directly — no base class change needed. This is the same approach Npgsql uses for PostgreSQL'sjsonb_set().Tested with MySQL 8.0.44. ASP.NET Core Identity PassKey rename operations now work correctly without any workaround.
Original prompt
This section details on the original issue you should resolve
<issue_title>Rename passkey error</issue_title>
<issue_description>Describe the bug
MySqlException: Invalid JSON text: "Invalid value." at position 0 in value for column 'AspNetUserPasskeys.Data'.
To Reproduce
Add Passkey and rename.
AspNetIdentityPasskeys
Expected behavior
In case it is not obvious from the bug description, please provide a clear and concise description of what you expected to happen.
Technical details (please complete the following information):
Additional context
An unhandled exception occurred while processing the request.
MySqlException: Invalid JSON text: "Invalid value." at position 0 in value for column 'AspNetUserPasskeys.Data'.
MySqlConnector.Core.ServerSession.ReceiveReplyAsync(IOBehavior ioBehavior, CancellationToken cancellationToken) in ServerSession.cs, line 1125
DbUpdateException: An error occurred while saving the entity changes. See the inner exception for details.
Microsoft.EntityFrameworkCore.Update.ReaderModificationCommandBatch.ExecuteAsync(IRelationalConnection connection, CancellationToken cancellationToken)
Stack Query Cookies Headers Routing
MySqlException: Invalid JSON text: "Invalid value." at position 0 in value for column 'AspNetUserPasskeys.Data'.
MySqlConnector.Core.ServerSession.ReceiveReplyAsync(IOBehavior ioBehavior, CancellationToken cancellationToken) in ServerSession.cs
System.Threading.Tasks.ValueTask.get_Result()
System.Runtime.CompilerServices.ConfiguredValueTaskAwaitable+ConfiguredValueTaskAwaiter.GetResult()
MySqlConnector.Core.ResultSet.ReadResultSetHeaderAsync(IOBehavior ioBehavior) in ResultSet.cs
MySqlConnector.MySqlDataReader.ActivateResultSet(CancellationToken cancellationToken) in MySqlDataReader.cs
MySqlConnector.MySqlDataReader.InitAsync(CommandListPosition commandListPosition, ICommandPayloadCreator payloadCreator, IDictionary<string, CachedProcedure> cachedProcedures, IMySqlCommand command, CommandBehavior behavior, Activity activity, IOBehavior ioBehavior, CancellationToken cancellationToken) in MySqlDataReader.cs
MySqlConnector.Core.CommandExecutor.ExecuteReaderAsync(CommandListPosition commandListPosition, ICommandPayloadCreator payloadCreator, CommandBehavior behavior, Activity activity, IOBehavior ioBehavior, CancellationToken cancellationToken) in CommandExecutor.cs
System.Runtime.CompilerServices.ConfiguredValueTaskAwaitable+ConfiguredValueTaskAwaiter.GetResult()
MySqlConnector.MySqlCommand.ExecuteReaderAsync(CommandBehavior behavior, IOBehavior ioBehavior, CancellationToken cancellationToken) in MySqlCommand.cs
System.Runtime.CompilerServices.ConfiguredValueTaskAwaitable+ConfiguredValueTaskAwaiter.GetResult()
MySqlConnector.MySqlCommand.ExecuteDbDataReaderAsync(CommandBehavior behavior, CancellationToken cancellationToken) in MySqlCommand.cs
Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken)
Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken)
Microsoft.EntityFrameworkCore.Update.ReaderModificationCommandBatch.ExecuteAsync(IRelationalConnection connection, CancellationToken cancellationToken)
Show raw exception details
DbUpdateException: An error occurred while saving the entity changes. See the inner exception for details.
Microsoft.EntityFrameworkCore.Update.ReaderModificationCommandBatch.ExecuteAsync(IRelationalConnection connection, CancellationToken cancellationToken)
Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.ExecuteAsync(IEnumerable commandBatches, IRelationalConnection connection, CancellationToken cancellationToken)
Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.ExecuteAsync(IEnumerable commandBatches, IRelationalConnection connection, CancellationToken cancellationToken)
Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.ExecuteAsync(IEnumerable commandBatches, IRelationalConnection connection, CancellationToken cancellationToken)
Microsoft.EntityFrameworkCore.Storage.RelationalDatabase.SaveChangesAsync(IList entries, CancellationToken cancellationToken)
Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChangesAsync(IList entriesToSave, CancellationToken cancellationToken)
Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChangesAsync(StateManager stateManager, bool acceptAllChangesOnSuccess, CancellationToken cancellationToke...
💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.