dotnet/efcore · error · InvalidOperationException
ExecuteOperationOnOwnedJsonIsNotSupported
ExecuteOperationOnOwnedJsonIsNotSupported
Error message
'{operation}' used over owned type '{entityType}' which is mapped to JSON; '{operation}' on JSON-mapped owned entities is not supported. Consider mapping your type as a complex type instead. What it means
After translating the property selector (line 317-321), EF checks whether the declaring entity type is an owned type mapped to JSON (IsMappedToJson). Bulk updates against an owned JSON entity are not translatable because they would require partial JSON document rewriting that EF does not support for owned entities; the error directs you to model the type as a complex type instead, which is the supported path for column-scoped bulk updates.
Source
Thrown at src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.ExecuteUpdate.cs:319
{
if (IsMemberAccess(expression, QueryCompilationContext.Model, out var baseExpression, out var member)
&& _sqlTranslator.TryBindMember(
_sqlTranslator.Visit(baseExpression), member, out var target, out var targetProperty))
{
translation = target;
property = targetProperty;
return true;
}
translation = null;
property = null;
return false;
}
}
if (targetProperty.DeclaringType is IEntityType entityType && entityType.IsMappedToJson())
{
throw new InvalidOperationException(
RelationalStrings.ExecuteOperationOnOwnedJsonIsNotSupported("ExecuteUpdate", entityType.DisplayName()));
}
// Hack: when returning a StructuralTypeShaperExpression, _sqlTranslator returns it wrapped by a
// StructuralTypeReferenceExpression, which is supposed to be a private wrapper only with the SQL translator.
// Call TranslateProjection to unwrap it (need to look into getting rid StructuralTypeReferenceExpression altogether).
if (target is not CollectionResultExpression)
{
target = _sqlTranslator.TranslateProjection(target) is { } unwrappedTarget
? unwrappedTarget
: throw new InvalidOperationException(RelationalStrings.InvalidPropertyInSetProperty(propertySelector.Print()));
}
switch (target)
{
case ColumnExpression column:
{
Check.DebugAssert(column.TypeMapping is not null);View on GitHub (pinned to dbf9771522)
Solutions
- Remodel the JSON-mapped owned type as a complex type (ComplexProperty/OwnsOne replaced by ComplexProperty with complex type), which supports ExecuteUpdate via flattened columns or JSON complex-type updates.
- If JSON mapping is required, do not bulk-update fields inside it; load the entities, mutate, and call SaveChanges.
- Update the entire owning row's JSON column via raw SQL if a partial JSON patch is truly needed.
Example fix
// before (Details is OwnsOne(...).ToJson())
db.Orders.ExecuteUpdate(s => s.SetProperty(
o => o.Details.Notes, "shipped"));
// after (remodel Details as a complex type so columns are updatable)
modelBuilder.Entity<Order>()
.ComplexProperty(o => o.Details);
// then ExecuteUpdate works on the flattened column
db.Orders.ExecuteUpdate(s => s.SetProperty(
o => o.Details.Notes, "shipped")); Defensive patterns
Strategy: validation
Validate before calling
// Inspect the model: is the target's declaring type a JSON-owned entity?
var prop = entityType.GetProperties().First(p => p.Name == "Notes");
var declaring = prop.DeclaringType as IEntityType;
bool jsonOwned = declaring is not null && declaring.IsMappedToJson();
if (jsonOwned) throw new InvalidOperationException("Remodel as a complex type before ExecuteUpdate."); Try / catch
try { await q.ExecuteUpdateAsync(s => s.SetProperty(e => e.Details.Notes, v)); }
catch (InvalidOperationException ex) when (ex.Message.Contains("mapped to JSON"))
{ /* switch the owned JSON type to a complex type, or use SaveChanges */ } Prevention
- Prefer complex types over JSON-mapped owned entities for data that needs bulk updates.
- Keep a single modeling style (complex vs owned-JSON) per updatable aggregate.
- Document which aggregates are bulk-updatable so contributors do not target JSON-owned fields.
When it happens
Trigger: SetProperty targeting a scalar property whose declaring type is an owned entity configured with ToJson (e.g. OwnsOne(x => x.Details, builder => builder.ToJson())). E.g. SetProperty(e => e.Details.Notes, "x") where Details is a JSON-owned owned entity.
Common situations: Migrating from owned-entity table-splitting to JSON mapping and re-running existing ExecuteUpdate calls; attempting to patch a single field inside a JSON-owned entity via ExecuteUpdate; using OwnsOne/OwnsMany + ToJson and trying bulk updates that worked before the JSON switch.
Related errors
- JsonExecuteUpdateNotSupportedWithOwnedEntities
- ExecuteUpdateOverJsonIsNotSupported
- ExecuteUpdateCannotSetJsonPropertyToNonJsonColumn
- ExecuteUpdateCannotSetJsonPropertyToArbitraryExpression
- IncompatibleComplexTypesInAssignment
AI-assisted analysis of dotnet/efcore@dbf9771522 (2026-08-06).
Data as JSON: /api/errors/24d4b62a7f04c16d.
Report an issue: GitHub.