dotnet/efcore · error · InvalidOperationException
The property ' . ' is mapped to a parameter of the stored…
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
Thrown when a DELETE stored procedure mapping has a parameter bound to a property that is neither a primary key part nor a concurrency token. Delete sprocs only identify the row(s) to remove, so EF restricts delete parameters to PK and row-version columns. Any other property mapped as a delete parameter is rejected.
Solutions
- Remove .HasParameter(p => p.NonKeyColumn) from the DeleteUsingStoredProcedure configuration.
- If the value is a row-version, mark the property as a concurrency token: builder.Property(p => p.RowVersion).IsRowVersion() / IsConcurrencyToken(), and use HasOriginalValueParameter.
- If you need additional filtering on delete, expose it as a non-property parameter (a literal/value parameter) rather than a property-bound one, or handle it inside the sproc body.
Example fix
// before
entityType.DeleteUsingStoredProcedure("DeleteOrder",
sp =>
{
sp.HasParameter(o => o.OrderId);
sp.HasParameter(o => o.Status); // Status is not key/concurrency token -> error
});
// after
entityType.DeleteUsingStoredProcedure("DeleteOrder",
sp => sp.HasParameter(o => o.OrderId)); Defensive patterns
Strategy: validation
Validate before calling
foreach (var et in model.GetEntityTypes())
{
var deleteSproc = et.GetDeleteStoredProcedure();
if (deleteSproc == null) continue;
foreach (var p in deleteSproc.Parameters)
{
if (p.PropertyName is string name)
{
var prop = et.FindProperty(name);
if (prop is { IsPrimaryKey: false } && prop?.IsConcurrencyToken == false)
Console.WriteLine($"{et.Name}.{name} cannot be a delete parameter (not key/concurrency).");
}
}
} Prevention
- Build delete sproc configs to accept only PK and row-version parameters.
- Use HasOriginalValueParameter for concurrency tokens on delete.
- Keep separate fluent blocks per sproc kind; never share one across insert/update/delete.
When it happens
Trigger: entityType.DeleteUsingStoredProcedure(...).HasParameter(p => p.SomeNonKeyColumn) where SomeNonKeyColumn is not part of the PK and not marked IsConcurrencyToken. Also surfaces when a current-value parameter (rather than original-value) is used on delete, or when a base-type property leaks into a derived delete sproc.
Common situations: Reusing a generic sproc configuration builder across insert/update/delete and forgetting to drop non-key parameters for delete; trying to pass an audit column to the delete sproc; mapping an FK column on delete.
Related errors
- A rows affected parameter, result column or return value…
- Both ' ' and ' ' are explicitly mapped to the stored…
- Both ' ' and ' ' are mapped to the stored procedure ' '…
- ' ' is mapped to the stored procedure ' ' while ' ' is…
- Input parameter ' ' of update stored procedure ' ' is…
AI-assisted analysis of dotnet/efcore@3a2006ef56 (2026-08-11).
Data as JSON: /api/errors/371459f107f5d181.
Report an issue: GitHub.
Appendix: 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 3a2006ef56)