dotnet/efcore · error · InvalidOperationException

A rows affected parameter, result column or return value can

Error message

A rows affected parameter, result column or return value cannot be configured on stored procedure '{sproc}' because it is used for insertion. Rows affected values are only allowed on stored procedures performing updating or deletion.

What it means

A 'rows affected' value (output parameter, result column, or return value) is used to detect optimistic-concurrency conflicts during Update/Delete. Inserts always succeed for a new row so a rows-affected signal is meaningless and not allowed. The validator at line 909-918 explicitly rejects any rows-affected configuration when the store object is an InsertStoredProcedure.

Source

Thrown at src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs:915

            }

            if (properties.Count > 0)
            {
                throw new InvalidOperationException(
                    RelationalStrings.StoredProcedurePropertiesNotMapped(
                        entityType.DisplayName(),
                        storeObjectIdentifier.DisplayName(),
                        properties.Values.Format()));
            }
        }

        if (sproc.IsRowsAffectedReturned
            || sproc.FindRowsAffectedParameter() != null
            || sproc.FindRowsAffectedResultColumn() != null)
        {
            if (storeObjectIdentifier.StoreObjectType == StoreObjectType.InsertStoredProcedure)
            {
                throw new InvalidOperationException(
                    RelationalStrings.StoredProcedureRowsAffectedForInsert(
                        storeObjectIdentifier.DisplayName()));
            }

            if (originalValueProperties.Values.FirstOrDefault(p => p.IsConcurrencyToken) is { } missedConcurrencyToken)
            {
                logger.StoredProcedureConcurrencyTokenNotMapped(entityType, missedConcurrencyToken, storeObjectIdentifier.DisplayName());
            }

            if (sproc.ResultColumns.Any(c => c != sproc.FindRowsAffectedResultColumn()))
            {
                throw new InvalidOperationException(
                    RelationalStrings.StoredProcedureRowsAffectedWithResultColumns(
                        entityType.DisplayName(),
                        storeObjectIdentifier.DisplayName()));
            }
        }
    }

View on GitHub (pinned to dbf9771522)

Solutions

  1. Remove the .RowsAffectedParameter() / .RowsAffectedResultColumn() / .ReturnsRowsAffected() call from the Insert sproc mapping.
  2. Move any rows-affected logic to the Update and/or Delete sproc mappings where it is meaningful.
  3. If you need to verify an insert succeeded, use the output identity value or wrap the call in a transaction with exception handling.

Example fix

// before
modelBuilder.Entity<Order>()
    .InsertStoredProcedure(o => o
        .Parameter(p => p.Amount, "amt")
        .RowsAffectedParameter("rc"));

// after
modelBuilder.Entity<Order>()
    .InsertStoredProcedure(o => o
        .Parameter(p => p.Amount, "amt")
        .OutputParameter(p => p.Id, "new_id"));
Defensive patterns

Strategy: validation

Validate before calling

foreach (var et in modelBuilder.Model.GetEntityTypes())
{
    var insert = et.GetStoredProcedures()
        .FirstOrDefault(s => s.StoreObjectType == StoreObjectType.InsertStoredProcedure);
    if (insert != null
        && (insert.IsRowsAffectedReturned
            || insert.FindRowsAffectedParameter() != null
            || insert.FindRowsAffectedResultColumn() != null))
    {
        throw new InvalidOperationException(
            $"Insert sproc for {et.Name} must not configure rows-affected.");
    }
}

Prevention

When it happens

Trigger: Calling `.InsertStoredProcedure(...).RowsAffectedParameter("rows")` / `.RowsAffectedResultColumn("rows")` / `.ReturnsRowsAffected()` (or returning rows-affected from the sproc).

Common situations: Reusing the same sproc builder snippet across insert/update/delete operations; assuming all sprocs need the rows-affected pattern that update/delete use for concurrency.

Related errors


AI-assisted analysis of dotnet/efcore@dbf9771522 (2026-08-06). Data as JSON: /api/errors/f7795bb35fae9de1. Report an issue: GitHub.