{"record":{"id":"371459f107f5d181","repo":"dotnet/efcore","slug":"the-property-entitytype-property-is-mapped-t-371459","errorCode":null,"errorMessage":"The property '{entityType}.{property}' is mapped to a parameter of the stored procedure '{sproc}', but only concurrency token and key properties are supported for Delete stored procedures.","messagePattern":"The property '(.+?)\\.(.+?)' is mapped to a parameter of the stored procedure '(.+?)', but only concurrency token and key properties are supported for Delete stored procedures\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs","lineNumber":818,"sourceCode":"                                && p.ForOriginalValue != parameter.ForOriginalValue\n                                && p.Direction != ParameterDirection.Input))\n                        {\n                            throw new InvalidOperationException(\n                                RelationalStrings.StoredProcedureOutputParameterConflict(\n                                    entityType.DisplayName(), parameter.PropertyName, storeObjectIdentifier.DisplayName()));\n                        }\n\n                        throw new InvalidOperationException(\n                            RelationalStrings.StoredProcedureOutputParameterNotGenerated(\n                                entityType.DisplayName(), parameter.PropertyName, storeObjectIdentifier.DisplayName()));\n                    }\n\n                    break;\n                case StoreObjectType.DeleteStoredProcedure:\n                    if (!property!.IsPrimaryKey()\n                        && !property.IsConcurrencyToken)\n                    {\n                        throw new InvalidOperationException(\n                            RelationalStrings.StoredProcedureDeleteNonKeyProperty(\n                                entityType.DisplayName(), parameter.PropertyName, storeObjectIdentifier.DisplayName()));\n                    }\n\n                    break;\n                default:\n                    Check.DebugFail(\"Unexpected stored procedure type: \" + storeObjectIdentifier.StoreObjectType);\n                    break;\n            }\n        }\n\n        foreach (var resultColumn in sproc.ResultColumns)\n        {\n            if (resultColumn.PropertyName == null)\n            {\n                continue;\n            }\n","sourceCodeStart":800,"sourceCodeEnd":836,"githubUrl":"https://github.com/dotnet/efcore/blob/dbf9771522148d61a2467854921bd5dc6f6e6916/src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs#L800-L836","documentation":"A Delete stored procedure may only receive key and concurrency-token properties as parameters, because deletion identifies the row by key (and optionally checks a concurrency token for optimistic concurrency). The validator at line 814-821 throws when a Delete sproc parameter maps to a property that is neither part of the primary key nor marked as a concurrency token.","triggerScenarios":"Configuring `.DeleteStoredProcedure(...)` and mapping a regular non-key property via `.Parameter(...)` (e.g. `.Parameter(p => p.Name, \"name\")`). The validator iterates each parameter and rejects any whose property fails `IsPrimaryKey()` and `IsConcurrencyToken`.","commonSituations":"Porting an existing `DELETE` sproc that takes extra WHERE columns into EF without trimming the parameter list; assuming EF will send full entity state to a delete sproc like it does for update.","solutions":["Remove the non-key/non-token parameters from the Delete sproc mapping so only PK and concurrency-token columns are sent.","If the sproc genuinely needs the extra column, move that logic into the key/concurrency check inside the sproc itself (e.g. compose the PK to include it) — but the EF mapping must only reference key/token properties.","If you need to delete using non-key criteria, use a raw SQL command or ExecuteDelete instead of a mapped delete sproc."],"exampleFix":"// before\nmodelBuilder.Entity<Order>()\n    .DeleteStoredProcedure(o => o\n        .Parameter(p => p.Id, \"id\")\n        .Parameter(p => p.CustomerId, \"cust_id\")); // CustomerId is not a key/token\n\n// after\nmodelBuilder.Entity<Order>()\n    .DeleteStoredProcedure(o => o\n        .Parameter(p => p.Id, \"id\")\n        .OriginalValueParameter(p => p.RowVersion, \"rowver\")); // concurrency token only","handlingStrategy":"validation","validationCode":"foreach (var et in modelBuilder.Model.GetEntityTypes())\n{\n    var deleteSproc = et.GetStoredProcedures()\n        .FirstOrDefault(s => s.StoreObjectType == StoreObjectType.DeleteStoredProcedure);\n    if (deleteSproc == null) continue;\n    foreach (var param in deleteSproc.Parameters)\n    {\n        if (param.PropertyName is not string name) continue;\n        var prop = et.FindProperty(name);\n        if (prop != null && !prop.IsPrimaryKey() && !prop.IsConcurrencyToken)\n        {\n            throw new InvalidOperationException(\n                $\"Delete sproc param '{param.Name}' maps non-key/non-token property '{name}'.\");\n        }\n    }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Restrict Delete sproc parameters to primary key and concurrency token properties only.","Use .OriginalValueParameter() for concurrency tokens so EF sends the loaded value.","Keep a checklist: Delete sproc = PK params (+ optional token) and nothing else."],"tags":["ef-core","stored-procedures","model-validation","delete"],"analyzedSha":"dbf9771522148d61a2467854921bd5dc6f6e6916","analyzedAt":"2026-08-06T20:46:03.226Z","schemaVersion":2},"datasetVersion":"2026-08-07T03:17:09.362Z"}