{"record":{"id":"2a179b0d87bfba99","repo":"dotnet/efcore","slug":"the-operation-operation-is-being-applied-on-en-2a179b","errorCode":null,"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.ExecuteUpdate.cs","lineNumber":43,"sourceCode":"    {\n        Check.DebugAssert(setters.Count > 0, \"Empty setters list\");\n\n        // Our source may have IncludeExpressions because of owned entities or auto-include; unwrap these, as they're meaningless for\n        // ExecuteUpdate's lambdas. Note that we don't currently support updates across tables.\n        source = source.UpdateShaperExpression(new IncludePruner().Visit(source.ShaperExpression));\n\n        var selectExpression = (SelectExpression)source.QueryExpression;\n\n        // Translate the setters: the left (property) selectors get translated to ColumnExpressions, the right (value) selectors to\n        // arbitrary SqlExpressions.\n        // Note that if the query isn't natively supported, we'll do a pushdown (see PushdownWithPkInnerJoinPredicate below); if that\n        // happens, we'll have to re-translate the setters over the new query (which includes a JOIN). However, we still translate here\n        // since we need the target table in order to perform the check below.\n        var translatedSetters = TranslateSetters(source, setters, out var targetTable);\n\n        if (targetTable is TpcTablesExpression tpcTablesExpression)\n        {\n            throw new InvalidOperationException(\n                RelationalStrings.ExecuteOperationOnTPC(\n                    nameof(EntityFrameworkQueryableExtensions.ExecuteUpdate),\n                    tpcTablesExpression.EntityType.DisplayName()));\n        }\n\n        // Check if the provider has a native translation for the update represented by the select expression.\n        // The default relational implementation handles simple, universally-supported cases (i.e. no operators except for predicate).\n        // Providers may override IsValidSelectExpressionForExecuteUpdate to add support for more cases via provider-specific UPDATE syntax.\n        if (IsValidSelectExpressionForExecuteUpdate(selectExpression, targetTable, out var tableExpression))\n        {\n            selectExpression.ReplaceProjection([]);\n            selectExpression.ApplyProjection();\n\n            return new UpdateExpression(tableExpression, selectExpression, translatedSetters);\n        }\n\n        return PushdownWithPkInnerJoinPredicate();\n","sourceCodeStart":25,"sourceCodeEnd":61,"githubUrl":"https://github.com/dotnet/efcore/blob/3a2006ef569de08368d59db5e1468aa8f407e4f8/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.ExecuteUpdate.cs#L25-L61","documentation":"TranslateExecuteUpdate resolves the target table from the setters; if it resolves to a TpcTablesExpression (the multi-table form produced when ExecuteUpdate is applied to a non-leaf TPC entity), the operation is refused. TPC non-leaf types span multiple concrete tables, and a single parametrized UPDATE cannot target them all.","triggerScenarios":"context.Set<TpcAbstract>().ExecuteUpdate(s => s.SetProperty(...)) where TpcAbstract is TPC-mapped and has derived types. The setters' entity resolves to the abstract type, producing a TpcTablesExpression that aggregates all concrete subtype tables.","commonSituations":"Bulk-updating against a TPC root or intermediate node; assuming ExecuteUpdate fans out across subtype tables.","solutions":["Target each concrete leaf subtype separately with its own ExecuteUpdate call.","Switch the hierarchy to TPH or a strategy that yields a single update target.","Load entities and use SaveChanges, or issue raw SQL UPDATEs against each concrete table."],"exampleFix":"// before (TPC non-leaf update -> 598)\nawait db.Set<TpcBase>()\n    .Where(b => b.Active)\n    .ExecuteUpdateAsync(s => s.SetProperty(b => b.UpdatedAt, DateTime.UtcNow));\n// after (per leaf type)\nawait db.Set<TpcLeafA>().Where(b => b.Active)\n    .ExecuteUpdateAsync(s => s.SetProperty(b => b.UpdatedAt, DateTime.UtcNow));\nawait db.Set<TpcLeafB>().Where(b => b.Active)\n    .ExecuteUpdateAsync(s => s.SetProperty(b => b.UpdatedAt, DateTime.UtcNow));","handlingStrategy":"validation","validationCode":"// Refuse ExecuteUpdate on non-leaf TPC types before invoking.\nvar et = db.Model.FindEntityType(typeof(TEntity))!;\nif (et.GetMappingStrategy() == RelationalAnnotationNames.TpcMappingStrategy\n    && et.GetDirectlyDerivedTypes().Any())\n{\n    throw new InvalidOperationException(\n        $\"{et.Name} is a non-leaf TPC type; call ExecuteUpdate per concrete leaf subtype.\");\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Apply ExecuteUpdate to concrete TPC leaf subtypes, not abstract roots.","Provide a helper that fans ExecuteUpdate across all leaf subtypes in a TPC hierarchy.","Reconsider TPC if hierarchy-wide bulk updates are frequent."],"tags":["efcore","executeupdate","tpc","inheritance","mapping"],"backgroundTag":null,"analyzedSha":"3a2006ef569de08368d59db5e1468aa8f407e4f8","analyzedAt":"2026-08-11T23:42:04.146Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}