dotnet/efcore · error · InvalidOperationException

'{operation}' used over owned type '{entityType}' which is m

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

Thrown when the property targeted by ExecuteUpdate/SetProperty belongs to an owned entity type that is mapped to JSON. Bulk updates inside JSON-mapped owned entities are not supported by the relational pipeline; the recommendation is to use complex types instead.

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 3a2006ef56)

Solutions

  1. Remodel the JSON-owned entity as a complex type (OwnsComplexType / ComplexProperty) which supports ExecuteUpdate over its scalar members.
  2. Fall back to loading the entities, mutating, and SaveChanges for JSON-owned entities.
  3. Move the property you need to update out of the JSON-owned entity into the owning entity's table.

Example fix

// before
modelBuilder.Entity<RootEntity>()
    .OwnsOne(r => r.RequiredAssociate, o => o.ToJson("Associate"));
await ctx.Set<RootEntity>().ExecuteUpdateAsync(s => s.SetProperty(r => r.RequiredAssociate.Name, "x"));
// after
modelBuilder.Entity<RootEntity>()
    .ComplexProperty(r => r.RequiredAssociate);
await ctx.Set<RootEntity>().ExecuteUpdateAsync(s => s.SetProperty(r => r.RequiredAssociate.Name, "x"));
Defensive patterns

Strategy: validation

Validate before calling

// Reject JSON-owned entities at config time when ExecuteUpdate is needed.
foreach (var et in ctx.Model.GetEntityTypes())
foreach (var nav in et.GetNavigations().Where(n => n.TargetEntityType.IsMappedToJson()))
{
    logger.LogWarning("{EntityType}.{Nav} is JSON-owned; ExecuteUpdate unsupported.", et.Name, nav.Name);
}

Prevention

When it happens

Trigger: Configuring an owned entity with ToJson(...) (e.g. OwnsOne(x => x.RequiredAssociate, o => o.ToJson())) and then calling ExecuteUpdate with a setter that targets a property of that owned entity. The OwnedJsonBulkUpdateRelationalTestBase asserts this for both ExecuteDelete and ExecuteUpdate.

Common situations: Porting an owned-JSON model and trying to bulk-update nested objects; mixing JSON-owned entities with ExecuteUpdate after upgrading EF Core.

Related errors


AI-assisted analysis of dotnet/efcore@3a2006ef56 (2026-08-11). Data as JSON: /api/errors/884893bdf2264cf9. Report an issue: GitHub.