dotnet/efcore · error · InvalidOperationException

' ' used over owned type ' ' which is mapped to JSON; ' '…

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

TranslateExecuteDelete checks entityType.IsMappedToJson() and refuses to delete JSON-mapped owned entities. JSON-mapped owned data lives as a JSON document inside the owner's column; deleting it independently of the owner is not a meaningful server operation in EF's model. The message suggests complex types as an alternative mapping.

Solutions

  1. Delete the owner entity (which removes its JSON document), or update the owner to null/remove the element from the collection via SaveChanges.
  2. If independent deletes are required, remap the owned type to its own table (ToTable) or use a complex type instead of .ToJson().
  3. Use raw SQL to mutate the JSON document if a partial JSON delete is needed (database-specific JSON functions).

Example fix

// before (delete JSON-mapped owned)
await db.Owners
    .SelectMany(o => o.Tags /* OwnsMany().ToJson() */)
    .ExecuteDeleteAsync();
// after (delete owner, or remap owned to a table)
await db.Owners.Where(o => o.Id == id).ExecuteDeleteAsync();
Defensive patterns

Strategy: validation

Validate before calling

// Block ExecuteDelete on JSON-mapped owned entities up front.
var et = db.Model.FindEntityType(typeof(TEntity))!;
if (et.IsMappedToJson())
    throw new InvalidOperationException(
        $"{et.Name} is JSON-mapped; delete the owner or remap to a table/complex type.");

Prevention

When it happens

Trigger: context.Owners.SelectMany(o => o.JsonCollection).ExecuteDelete() where JsonCollection is an OwnsMany(...).ToJson() navigation; calling ExecuteDelete on a query rooted at a JSON-mapped owned entity; targeting an owned entity mapped via .ToJson().

Common situations: Treating a JSON collection as a regular related entity and attempting bulk delete; refactoring from table-splitting to JSON mapping and reusing an existing ExecuteDelete call.

Related errors


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

Appendix: source

Thrown at src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.ExecuteDelete.cs:22

using Microsoft.EntityFrameworkCore.Query.SqlExpressions;

namespace Microsoft.EntityFrameworkCore.Query;

public partial class RelationalQueryableMethodTranslatingExpressionVisitor
{
    /// <inheritdoc />
    protected override DeleteExpression TranslateExecuteDelete(ShapedQueryExpression source)
    {
        source = source.UpdateShaperExpression(new IncludePruner().Visit(source.ShaperExpression));

        if (source.ShaperExpression is not StructuralTypeShaperExpression { StructuralType: IEntityType entityType } shaper)
        {
            throw new InvalidOperationException(RelationalStrings.ExecuteDeleteOnNonEntityType);
        }

        if (entityType.IsMappedToJson())
        {
            throw new InvalidOperationException(
                RelationalStrings.ExecuteOperationOnOwnedJsonIsNotSupported("ExecuteDelete", entityType.DisplayName()));
        }

        switch (entityType.GetMappingStrategy())
        {
            case RelationalAnnotationNames.TptMappingStrategy:
                throw new InvalidOperationException(
                    RelationalStrings.ExecuteOperationOnTPT(
                        nameof(EntityFrameworkQueryableExtensions.ExecuteDelete),
                        entityType.DisplayName()));

            // Note that we do allow TPC if the target is a leaf type
            case RelationalAnnotationNames.TpcMappingStrategy when entityType.GetDirectlyDerivedTypes().Any():
                throw new InvalidOperationException(
                    RelationalStrings.ExecuteOperationOnTPC(
                        nameof(EntityFrameworkQueryableExtensions.ExecuteDelete),
                        entityType.DisplayName()));
        }

View on GitHub (pinned to 3a2006ef56)