{"record":{"id":"9d658c9957aaf581","repo":"dotnet/efcore","slug":"the-operation-executedelete-requires-an-entity-t","errorCode":null,"errorMessage":"The operation 'ExecuteDelete' requires an entity type which corresponds to the database table to be modified. The current operation is being applied on a non-entity projection. Remove any projection to non-entity types.","messagePattern":"The operation 'ExecuteDelete' requires an entity type which corresponds to the database table to be modified\\. The current operation is being applied on a non-entity projection\\. Remove any projection to non-entity types\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.ExecuteDelete.cs","lineNumber":17,"sourceCode":"// Licensed to the .NET Foundation under one or more agreements.\n// The .NET Foundation licenses this file to you under the MIT license.\n\nusing Microsoft.EntityFrameworkCore.Query.SqlExpressions;\n\nnamespace Microsoft.EntityFrameworkCore.Query;\n\npublic partial class RelationalQueryableMethodTranslatingExpressionVisitor\n{\n    /// <inheritdoc />\n    protected override DeleteExpression TranslateExecuteDelete(ShapedQueryExpression source)\n    {\n        source = source.UpdateShaperExpression(new IncludePruner().Visit(source.ShaperExpression));\n\n        if (source.ShaperExpression is not StructuralTypeShaperExpression { StructuralType: IEntityType entityType } shaper)\n        {\n            throw new InvalidOperationException(RelationalStrings.ExecuteDeleteOnNonEntityType);\n        }\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():","sourceCodeStart":1,"sourceCodeEnd":35,"githubUrl":"https://github.com/dotnet/efcore/blob/3a2006ef569de08368d59db5e1468aa8f407e4f8/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.ExecuteDelete.cs#L1-L35","documentation":"TranslateExecuteDelete runs an IncludePruner over the shaper and then requires the result to be a StructuralTypeShaperExpression whose StructuralType is an IEntityType. If the shaper is anything else (anonymous/DTO projection, complex type, value type), there is no entity table to delete from and ExecuteDelete cannot be translated.","triggerScenarios":"context.Blogs.Select(b => new BlogDto { ... }).ExecuteDelete(); context.Blogs.Select(b => new { b.Id, b.Title }).ExecuteDelete(); calling ExecuteDelete after a projection that replaces the entity shaper with a non-entity shaper; calling ExecuteDelete over a query rooted on a complex type or keyless view projection.","commonSituations":"Chaining ExecuteDelete after Select/SelectMany that projects to a DTO or anonymous type; refactoring a query and forgetting that ExecuteDelete must target an entity; mixing read-model projections with bulk-delete intent.","solutions":["Apply ExecuteDelete directly on the DbSet/entity query before any non-entity projection: context.Blogs.Where(...).ExecuteDelete().","Move the projection after deletion is unnecessary; if you only need to filter, express the filter with Where on the entity, not Select.","If you must compute the set of keys first, materialize keys to memory then issue a single ExecuteDelete with Contains on the entity: context.Blogs.Where(b => keys.Contains(b.Id)).ExecuteDelete()."],"exampleFix":"// before (projection before delete -> 590)\nawait db.Blogs\n    .Select(b => new BlogDto { Id = b.Id })\n    .ExecuteDeleteAsync();\n// after (delete on entity, filter with Where)\nawait db.Blogs\n    .Where(b => b.Archived)\n    .ExecuteDeleteAsync();","handlingStrategy":"validation","validationCode":"// Ensure ExecuteDelete is applied to an entity query, not a projection.\nvar elementType = typeof(TSource);\nvar isEntity = db.Model.FindEntityType(elementType) is not null;\nif (!isEntity)\n    throw new InvalidOperationException($\"{elementType} is not a mapped entity; call ExecuteDelete on the entity before projecting.\");","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Call ExecuteDelete on the DbSet/Where chain before any Select.","When you need a computed set of keys, materialize them and use Contains in the entity-side Where.","Review refactors that introduce DTOs into delete/update pipelines."],"tags":["efcore","executedelete","projection","query-translation"],"backgroundTag":null,"analyzedSha":"3a2006ef569de08368d59db5e1468aa8f407e4f8","analyzedAt":"2026-08-11T23:42:04.146Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}