dotnet/efcore · error · InvalidOperationException
ExecuteUpdate over JSON columns is not supported when the co
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
Thrown by ProcessStructuralJsonSetter when the JsonQueryExpression's StructuralType is not an IComplexType — i.e. the JSON column is mapped as an owned entity rather than a complex type. ExecuteUpdate over JSON columns only supports the complex-type mapping; owned-entity JSON columns are rejected.
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 3a2006ef56)
Solutions
- Map the JSON column as a complex type (ComplexProperty) instead of an owned entity.
- For owned-entity JSON, fall back to load-modify-SaveChanges.
- Split the JSON-owned entity into scalar properties you can set individually if they must be columns.
Example fix
// before
modelBuilder.Entity<Root>().OwnsOne(r => r.Info, o => o.ToJson("info"));
await ctx.Set<Root>().ExecuteUpdateAsync(s => s.SetProperty(r => r.Info, someValue));
// after
modelBuilder.Entity<Root>().ComplexProperty(r => r.Info);
await ctx.Set<Root>().ExecuteUpdateAsync(s => s.SetProperty(r => r.Info, someValue)); Defensive patterns
Strategy: validation
Validate before calling
// Flag JSON columns mapped as owned entities (instead of complex types).
foreach (var et in ctx.Model.GetEntityTypes())
foreach (var nav in et.GetNavigations().Where(n => n.TargetEntityType.IsMappedToJson()))
logger.LogWarning("JSON column for {Nav} is owned; map as complex type for ExecuteUpdate.", nav.Name); Prevention
- Map JSON columns as complex types when whole-object bulk update is needed.
- Migrate owned-JSON to complex-JSON where possible.
- Use SaveChanges for owned-JSON updates.
When it happens
Trigger: OwnsOne/OwnsMany with ToJson on an entity, then ExecuteUpdate that reaches ProcessStructuralJsonSetter (whole-JSON-object setter path). The structural type resolved is the owned entity type, not a complex type, so the check `is not IComplexType` fires.
Common situations: Pre-complex-type owned-JSON models attempting bulk updates; mixing OwnsOne-ToJson with ExecuteUpdate on the whole JSON object.
Related errors
- 'ExecuteUpdate' is being used over type '{structuralType}' w
- 'ExecuteUpdate' is being used over a LINQ operator which isn
- '{operation}' used over owned type '{entityType}' which is m
- 'ExecuteUpdate' cannot currently set a property in a JSON co
- 'ExecuteUpdate' cannot currently set a property in a JSON co
AI-assisted analysis of dotnet/efcore@3a2006ef56 (2026-08-11).
Data as JSON: /api/errors/44dab5ee8221ecba.
Report an issue: GitHub.