dotnet/efcore · error · InvalidOperationException

JsonExecuteUpdateNotSupportedWithOwnedEntities

JsonExecuteUpdateNotSupportedWithOwnedEntities

Error message

ExecuteUpdate over JSON columns is not supported when the column is mapped as an owned entity. Map the column as a complex type instead.

What it means

ProcessStructuralJsonSetter (line 663-739) handles updating a JSON document through a JsonQueryExpression. It only supports complex types (IComplexType). If the JSON structural type is an owned entity type (not a complex type), partial JSON updates are unsupported and JsonExecuteUpdateNotSupportedWithOwnedEntities is thrown at line 668-670, with a message recommending complex types.

Source

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

                            StructuralTypeShaperExpression
                            {
                                StructuralType: IComplexType,
                                ValueBufferExpression: StructuralTypeProjectionExpression projection
                            }
                                => projection.BindComplexProperty(complexProperty),

                            _ => throw new UnreachableException()
                        };
                }
            }

            void ProcessStructuralJsonSetter(JsonQueryExpression jsonQuery)
            {
                var jsonColumn = jsonQuery.JsonColumn;

                if (jsonQuery.StructuralType is not IComplexType complexType)
                {
                    throw new InvalidOperationException(RelationalStrings.JsonExecuteUpdateNotSupportedWithOwnedEntities);
                }

                Check.DebugAssert(jsonColumn.TypeMapping is not null);

                ProcessColumn(jsonColumn, targetProperty);

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

                SqlExpression? serializedValue;

                switch (translatedValue)
                {
                    // When an object is instantiated inline (e.g. SetProperty(c => c.ShippingAddress, c => new Address { ... })), we get a SqlConstantExpression
                    // with the .NET instance. Serialize it to JSON and replace the constant (note that the type mapping is inferred from the
                    // JSON column on other side - important for e.g. nvarchar vs. json columns)
                    case SqlConstantExpression { Value: var value }:
                        serializedValue = new SqlConstantExpression(
                            RelationalJsonUtilities.SerializeComplexTypeToJson(complexType, value, jsonQuery.IsCollection),

View on GitHub (pinned to dbf9771522)

Solutions

  1. Remodel the JSON-mapped owned entity as a complex type (ComplexProperty) so partial JSON ExecuteUpdate is supported.
  2. For owned JSON entities, load and SaveChanges instead of ExecuteUpdate.
  3. Update the entire JSON column via raw SQL if a partial update is required.

Example fix

// before (Owned is OwnsOne(...).ToJson())
modelBuilder.Entity<Order>().OwnsOne(o => o.Details, d => d.ToJson());
db.Orders.ExecuteUpdate(s => s.SetProperty(
    o => o.Details.Notes, "x"));

// after (Details is a complex type)
modelBuilder.Entity<Order>().ComplexProperty(o => o.Details);
db.Orders.ExecuteUpdate(s => s.SetProperty(
    o => o.Details.Notes, "x"));
Defensive patterns

Strategy: validation

Validate before calling

// Confirm the JSON structural type is a complex type, not an owned entity.
bool isComplex = jsonStructuralType is IComplexType;
if (!isComplex) throw new InvalidOperationException("Remodel JSON owned entity as a complex type for ExecuteUpdate.");

Try / catch

try { await q.ExecuteUpdateAsync(s => s.SetProperty(e => e.Details.X, v)); }
catch (InvalidOperationException ex) when (ex.Message.Contains("owned entities"))
{ /* switch OwnsOne().ToJson() to ComplexProperty, or use SaveChanges */ }

Prevention

When it happens

Trigger: ExecuteUpdate whose property selector resolves to a JSON-mapped owned entity (OwnsOne/OwnsMany with ToJson), targeting either the whole owned entity or a path inside it, where the structural type at that path is an owned entity rather than a complex type.

Common situations: Pre-complex-type models that use OwnsOne().ToJson() and attempt ExecuteUpdate on the JSON document or sub-paths; migrating an owned JSON entity and expecting ExecuteUpdate support that only complex types get.

Related errors


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