{"record":{"id":"def0241f8d0daa93","repo":"dotnet/efcore","slug":"executedeleteonnonentitytype","errorCode":"ExecuteDeleteOnNonEntityType","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/dbf9771522148d61a2467854921bd5dc6f6e6916/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.ExecuteDelete.cs#L1-L35","documentation":"Thrown by TranslateExecuteDelete when, after pruning includes, the query shaper does not project an IEntityType - e.g. it projects an anonymous type, a DTO, or a scalar. ExecuteDelete must target a real entity mapped to a table; it cannot operate on an arbitrary projection.","triggerScenarios":"Calling ExecuteDelete on a query that projects to a non-entity shape: db.Blogs.Select(b => new { b.Id }).ExecuteDeleteAsync(), or db.Blogs.Select(b => b.Name).ExecuteDeleteAsync(), or projecting a complex type / owned type directly.","commonSituations":"Composing Select/anonymous-type projections before ExecuteDelete; reusing a query builder that returns DTOs; misunderstanding that ExecuteDelete needs the entity shape, not a derived projection.","solutions":["Remove the Select/anonymous projection so the query returns the entity type directly: db.Blogs.ExecuteDeleteAsync().","If you projected for filtering, move the filter into Where and drop the Select: db.Blogs.Where(b => ids.Contains(b.Id)).ExecuteDeleteAsync().","Ensure the query root is the entity DbSet rather than a computed projection.","If you must delete based on a DTO, resolve the entity keys and delete via the entity DbSet."],"exampleFix":"// before\nawait db.Blogs.Select(b => new Dto { Id = b.Id }).ExecuteDeleteAsync();\n// after\nawait db.Blogs.ExecuteDeleteAsync();\n// or with a filter instead of a projection\nawait db.Blogs.Where(b => b.Rating < 3).ExecuteDeleteAsync();","handlingStrategy":"validation","validationCode":"// Ensure no projection (Select/anonymous) sits between the DbSet and ExecuteDelete\nvar query = db.Blogs.Where(b => b.Rating < 3); // entity-typed, not projected\nawait query.ExecuteDeleteAsync();","typeGuard":null,"tryCatchPattern":"try { await query.ExecuteDeleteAsync(); }\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"non-entity projection\"))\n{\n    // re-issue against the entity DbSet with just a predicate\n    await db.Blogs.Where(predicate).ExecuteDeleteAsync();\n}","preventionTips":["Never put Select between a DbSet and ExecuteDelete/ExecuteUpdate.","Move filtering logic into Where, not Select.","Ensure the query root is the entity DbSet.","Write integration tests covering each bulk delete path."],"tags":["executedelete","projection","entity-type","bulk-operations"],"analyzedSha":"dbf9771522148d61a2467854921bd5dc6f6e6916","analyzedAt":"2026-08-06T20:46:03.226Z","schemaVersion":2},"datasetVersion":"2026-08-07T03:17:09.362Z"}