dotnet/efcore · error · InvalidOperationException

ExecuteUpdateOverJsonIsNotSupported

ExecuteUpdateOverJsonIsNotSupported

Error message

'ExecuteUpdate' is being used over type '{structuralType}' which is mapped to JSON; 'ExecuteUpdate' on JSON is not supported.

What it means

In the StructuralTypeShaperExpression case for complex types (line 349-369), if the target complex type itself IsMappedToJson, ExecuteUpdate cannot produce a meaningful update for it and throws ExecuteUpdateOverJsonIsNotSupported. JSON-mapped complex types are not writable through ExecuteUpdate at the whole-type level.

Source

Thrown at src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.ExecuteUpdate.cs:361

                    translatedSetters.Add(new ColumnValueSetter(column, translatedValue));
                    break;
                }

                // A table-split complex type is being assigned a new value.
                // Generate setters for each of the columns mapped to the comlex type.
                case StructuralTypeShaperExpression
                {
                    StructuralType: IComplexType complexType,
                    ValueBufferExpression: StructuralTypeProjectionExpression
                } shaper:
                {
                    Check.DebugAssert(
                        targetProperty is IComplexProperty complexProperty && complexProperty.ComplexType == complexType,
                        "PropertyBase should be a complex property referring to the correct complex type");

                    if (complexType.IsMappedToJson())
                    {
                        throw new InvalidOperationException(
                            RelationalStrings.ExecuteUpdateOverJsonIsNotSupported(complexType.DisplayName()));
                    }

                    var translatedValue = TranslateSetterValueSelector(source, valueSelector, shaper.Type);
                    ProcessComplexType(shaper, translatedValue);

                    break;
                }

                case JsonScalarExpression { Json: ColumnExpression jsonColumn } jsonScalar:
                {
                    var typeMapping = jsonScalar.TypeMapping;
                    Check.DebugAssert(typeMapping is not null);

                    // We should never see a JsonScalarExpression without a path - that means we're mapping a JSON scalar directly to a relational column.
                    // This is in theory possible (e.g. map a DateTime to a 'json' column with a single string timestamp representation inside, instead of to
                    // SQL Server datetime2), but contrived and unsupported.
                    Check.DebugAssert(jsonScalar.Path.Count > 0);

View on GitHub (pinned to dbf9771522)

Solutions

  1. If the complex type must be updatable via ExecuteUpdate, map it as a table-split complex type (flattened columns) instead of JSON.
  2. Update individual scalar columns instead of the whole complex value.
  3. Fall back to load + mutate + SaveChanges for JSON complex types.

Example fix

// before (Address is ComplexProperty(...).ToJson())
db.Contacts.ExecuteUpdate(s => s.SetProperty(
    c => c.Address, new Address { City = "NYC" }));

// after (map Address as flattened complex columns, not JSON)
modelBuilder.Entity<Contact>().ComplexProperty(c => c.Address);
db.Contacts.ExecuteUpdate(s => s.SetProperty(
    c => c.Address.City, "NYC"));
Defensive patterns

Strategy: validation

Validate before calling

// Check whether the target complex type is JSON-mapped before assigning it whole.
var cp = entityType.GetComplexProperties().FirstOrDefault(p => p.Name == "Address");
bool jsonComplex = cp?.ComplexType.IsMappedToJson() == true;
if (jsonComplex) throw new InvalidOperationException("Use flattened complex type or update scalars.");

Try / catch

try { await q.ExecuteUpdateAsync(s => s.SetProperty(e => e.Address, value)); }
catch (InvalidOperationException ex) when (ex.Message.Contains("mapped to JSON"))
{ /* remap as flattened complex type or update scalar sub-properties */ }

Prevention

When it happens

Trigger: SetProperty assigning an entire JSON-mapped complex type value, e.g. SetProperty(e => e.JsonComplexProperty, new MyComplexType { ... }) where the complex property is configured to live inside a JSON column.

Common situations: Using ComplexProperty(...).ToJson() (JSON-mapped complex type) and attempting a whole-value ExecuteUpdate on it; mixing table-split complex types with JSON complex types and reusing the same update code for both.

Related errors


AI-assisted analysis of dotnet/efcore@dbf9771522 (2026-08-06). Data as JSON: /api/errors/f90f3477f97844ba. Report an issue: GitHub.