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

  1. Map the JSON column as a complex type (ComplexProperty) instead of an owned entity.
  2. For owned-entity JSON, fall back to load-modify-SaveChanges.
  3. 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

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


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