dotnet/efcore · error · InvalidOperationException
The property '{entityType}.{property}' is mapped to a parame
Error message
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. What it means
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.
Source
Thrown at src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs:818
&& p.ForOriginalValue != parameter.ForOriginalValue
&& p.Direction != ParameterDirection.Input))
{
throw new InvalidOperationException(
RelationalStrings.StoredProcedureOutputParameterConflict(
entityType.DisplayName(), parameter.PropertyName, storeObjectIdentifier.DisplayName()));
}
throw new InvalidOperationException(
RelationalStrings.StoredProcedureOutputParameterNotGenerated(
entityType.DisplayName(), parameter.PropertyName, storeObjectIdentifier.DisplayName()));
}
break;
case StoreObjectType.DeleteStoredProcedure:
if (!property!.IsPrimaryKey()
&& !property.IsConcurrencyToken)
{
throw new InvalidOperationException(
RelationalStrings.StoredProcedureDeleteNonKeyProperty(
entityType.DisplayName(), parameter.PropertyName, storeObjectIdentifier.DisplayName()));
}
break;
default:
Check.DebugFail("Unexpected stored procedure type: " + storeObjectIdentifier.StoreObjectType);
break;
}
}
foreach (var resultColumn in sproc.ResultColumns)
{
if (resultColumn.PropertyName == null)
{
continue;
}
View on GitHub (pinned to dbf9771522)
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.
Example fix
// before
modelBuilder.Entity<Order>()
.DeleteStoredProcedure(o => o
.Parameter(p => p.Id, "id")
.Parameter(p => p.CustomerId, "cust_id")); // CustomerId is not a key/token
// after
modelBuilder.Entity<Order>()
.DeleteStoredProcedure(o => o
.Parameter(p => p.Id, "id")
.OriginalValueParameter(p => p.RowVersion, "rowver")); // concurrency token only Defensive patterns
Strategy: validation
Validate before calling
foreach (var et in modelBuilder.Model.GetEntityTypes())
{
var deleteSproc = et.GetStoredProcedures()
.FirstOrDefault(s => s.StoreObjectType == StoreObjectType.DeleteStoredProcedure);
if (deleteSproc == null) continue;
foreach (var param in deleteSproc.Parameters)
{
if (param.PropertyName is not string name) continue;
var prop = et.FindProperty(name);
if (prop != null && !prop.IsPrimaryKey() && !prop.IsConcurrencyToken)
{
throw new InvalidOperationException(
$"Delete sproc param '{param.Name}' maps non-key/non-token property '{name}'.");
}
}
} Prevention
- 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.
When it happens
Trigger: 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`.
Common situations: 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.
Related errors
- The property '{entityType}.{property}' is mapped to a result
- Current value parameter '{parameter}' is not allowed on dele
- The entity type '{entityType}' was configured to use some st
- The entity type '{entityType}' was configured to use '{sproc
- Both entity type '{entityType1}' and '{entityType2}' were co
AI-assisted analysis of dotnet/efcore@dbf9771522 (2026-08-06).
Data as JSON: /api/errors/371459f107f5d181.
Report an issue: GitHub.