{"record":{"id":"79714ac0cbde0173","repo":"dotnet/efcore","slug":"executeoperationontpc","errorCode":"ExecuteOperationOnTPC","errorMessage":"The operation '{operation}' is being applied on entity type '{entityType}', which is using the TPC mapping strategy and is not a leaf type. 'ExecuteDelete'/'ExecuteUpdate' operations on entity types participating in TPC hierarchies is only supported for leaf types.","messagePattern":"The operation '(.+?)' is being applied on entity type '(.+?)', which is using the TPC mapping strategy and is not a leaf type\\. 'ExecuteDelete'/'ExecuteUpdate' operations on entity types participating in TPC hierarchies is only supported for leaf types\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.ExecuteDelete.cs","lineNumber":36,"sourceCode":"        }\n\n        if (entityType.IsMappedToJson())\n        {\n            throw new InvalidOperationException(\n                RelationalStrings.ExecuteOperationOnOwnedJsonIsNotSupported(\"ExecuteDelete\", entityType.DisplayName()));\n        }\n\n        switch (entityType.GetMappingStrategy())\n        {\n            case RelationalAnnotationNames.TptMappingStrategy:\n                throw new InvalidOperationException(\n                    RelationalStrings.ExecuteOperationOnTPT(\n                        nameof(EntityFrameworkQueryableExtensions.ExecuteDelete),\n                        entityType.DisplayName()));\n\n            // Note that we do allow TPC if the target is a leaf type\n            case RelationalAnnotationNames.TpcMappingStrategy when entityType.GetDirectlyDerivedTypes().Any():\n                throw new InvalidOperationException(\n                    RelationalStrings.ExecuteOperationOnTPC(\n                        nameof(EntityFrameworkQueryableExtensions.ExecuteDelete),\n                        entityType.DisplayName()));\n        }\n\n        // Find the table model that maps to the entity type; there must be exactly one (e.g. no entity splitting).\n        var targetTable = entityType.GetTableMappings().ToList() switch\n        {\n            [] => throw new InvalidOperationException(\n                RelationalStrings.ExecuteUpdateDeleteOnEntityNotMappedToTable(entityType.DisplayName())),\n            [var singleTableMapping] => singleTableMapping.Table,\n            _ => throw new InvalidOperationException(\n                RelationalStrings.ExecuteOperationOnEntitySplitting(\n                    nameof(EntityFrameworkQueryableExtensions.ExecuteDelete), entityType.DisplayName())),\n        };\n        var selectExpression = (SelectExpression)source.QueryExpression;\n\n        // Find the table expression in the SelectExpression that corresponds to the projected entity type.","sourceCodeStart":18,"sourceCodeEnd":54,"githubUrl":"https://github.com/dotnet/efcore/blob/dbf9771522148d61a2467854921bd5dc6f6e6916/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.ExecuteDelete.cs#L18-L54","documentation":"Thrown by TranslateExecuteDelete when the target entity type uses the TPC (Table-Per-Concrete-Type) strategy and is NOT a leaf - i.e. it has derived types. In TPC, ExecuteDelete/ExecuteUpdate can only safely target a leaf type (one concrete table); a non-leaf has no single concrete table to modify, so derived rows elsewhere would be missed.","triggerScenarios":"Configuring a hierarchy with .UseTpcMappingStrategy() and calling ExecuteDelete/ExecuteUpdate on a base or intermediate type that has directly derived types.","commonSituations":"Choosing TPC and then trying to bulk-delete from the root entity; targeting an abstract base or a non-leaf node.","solutions":["Target a leaf type in the TPC hierarchy (a type with no derived types).","Issue separate ExecuteDelete calls per concrete leaf type, or union them explicitly.","Switch the hierarchy to TPH for full base-type bulk delete support.","Load and delete via SaveChanges if you must delete across the whole TPC hierarchy."],"exampleFix":"// before - Animal is the TPC root, has derived Dog/Cat\nawait db.Animals.Where(a => a.Id == id).ExecuteDeleteAsync();\n// after - delete each leaf explicitly, or query the right leaf\nawait db.Set<Dog>().Where(d => d.Id == id).ExecuteDeleteAsync();\nawait db.Set<Cat>().Where(c => c.Id == id).ExecuteDeleteAsync();","handlingStrategy":"validation","validationCode":"static bool IsTpcLeaf(IEntityType et)\n    => et.GetMappingStrategy() == RelationalAnnotationNames.TpcMappingStrategy\n       && !et.GetDirectlyDerivedTypes().Any();\n\nif (!IsTpcLeaf(entityType))\n    throw new InvalidOperationException(\"ExecuteDelete on TPC requires a leaf type.\");","typeGuard":null,"tryCatchPattern":"try { await db.Animals.Where(predicate).ExecuteDeleteAsync(); }\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"TPC mapping strategy\"))\n{\n    // fan out to leaf types explicitly\n    await db.Set<Dog>().Where(predicate).ExecuteDeleteAsync();\n    await db.Set<Cat>().Where(predicate).ExecuteDeleteAsync();\n}","preventionTips":["Target leaf types when bulk-deleting in TPC hierarchies.","Add a helper that enumerates concrete leaves and deletes each.","Use TPH when you need to delete from the hierarchy root.","Write tests covering base-vs-leaf delete in TPC."],"tags":["executedelete","executeupdate","inheritance","tpc","bulk-operations"],"analyzedSha":"dbf9771522148d61a2467854921bd5dc6f6e6916","analyzedAt":"2026-08-06T20:46:03.226Z","schemaVersion":2},"datasetVersion":"2026-08-07T02:17:10.218Z"}