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

  1. Remove .HasParameter(p => p.NonKeyColumn) from the DeleteUsingStoredProcedure configuration.
  2. If the value is a row-version, mark the property as a concurrency token: builder.Property(p => p.RowVersion).IsRowVersion() / IsConcurrencyToken(), and use HasOriginalValueParameter.
  3. 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

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


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)