dotnet/efcore · error · InvalidOperationException

The operation '{operation}' is being applied on entity type

Error message

The operation '{operation}' is being applied on entity type '{entityType}', which is using the TPT mapping strategy. 'ExecuteDelete'/'ExecuteUpdate' operations on hierarchies mapped as TPT are not supported.

What it means

TranslateExecuteDelete switches on GetMappingStrategy() and throws for TPT (table-per-type). In TPT each type has its own table, so a single parametrized DELETE statement cannot correctly remove a hierarchy's rows across multiple tables; EF therefore refuses ExecuteDelete/ExecuteUpdate on TPT hierarchies.

Source

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

    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()));
        }

        // Find the table model that maps to the entity type; there must be exactly one (e.g. no entity splitting).
        var targetTable = entityType.GetTableMappings().ToList() switch
        {
            [] => throw new InvalidOperationException(
                RelationalStrings.ExecuteUpdateDeleteOnEntityNotMappedToTable(entityType.DisplayName())),
            [var singleTableMapping] => singleTableMapping.Table,

View on GitHub (pinned to 3a2006ef56)

Solutions

  1. Switch the hierarchy to TPH (single table) or TPC if bulk delete/update is required.
  2. Issue per-table raw SQL deletes (base + each subtype) in the correct order via Database.ExecuteSqlRaw.
  3. Load entities and use RemoveRange + SaveChanges, which correctly deletes across TPT tables.
  4. Target a specific concrete leaf type with its own mapping that is not TPT.

Example fix

// before (TPT hierarchy)
await db.Set<TptBase>().Where(b => b.Stale).ExecuteDeleteAsync();
// after (per-table raw SQL or SaveChanges)
foreach (var id in ids) { var e = await db.Set<TptBase>().FindAsync(id); if (e != null) db.Remove(e); }
await db.SaveChangesAsync();
Defensive patterns

Strategy: validation

Validate before calling

// Reject ExecuteDelete/ExecuteUpdate on TPT hierarchies before calling.
var et = db.Model.FindEntityType(typeof(TEntity))!;
var strategy = et.GetMappingStrategy();
if (strategy == RelationalAnnotationNames.TptMappingStrategy)
    throw new InvalidOperationException(
        "ExecuteDelete/ExecuteUpdate is not supported on TPT hierarchies; use TPH/TPC or SaveChanges/raw SQL.");

Prevention

When it happens

Trigger: context.Set<TptBase>().ExecuteDelete() (or on a derived type) where the hierarchy is mapped with .UseTptMappingStrategy(); similarly ExecuteUpdate on the same. Triggered as soon as the target entity type's mapping strategy resolves to TPT.

Common situations: Existing TPT hierarchies where a developer expects bulk delete to work; switching a hierarchy from TPH to TPT and re-running a previously-working ExecuteDelete.

Related errors


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