dotnet/efcore · error · InvalidOperationException

The rows affected result column cannot be added to the store

Error message

The rows affected result column cannot be added to the stored procedure '{sproc}' because the rows affected are already returned via another column, via a parameter or via the stored procedure return value.

What it means

Thrown by StoredProcedure.AddRowsAffectedResultColumn when any rows-affected capture already exists — a rows-affected result column, a rows-affected parameter, or the IsRowsAffectedReturned flag. EF allows exactly one rows-affected channel per stored procedure.

Source

Thrown at src/EFCore.Relational/Metadata/Internal/StoredProcedure.cs:653

    ///     any release. You should only use it directly in your code with extreme caution and knowing that
    ///     doing so can result in application failures when updating to a new Entity Framework Core release.
    /// </summary>
    public virtual StoredProcedureResultColumn? FindRowsAffectedResultColumn()
        => _rowsAffectedResultColumn;

    /// <summary>
    ///     This is an internal API that supports the Entity Framework Core infrastructure and not subject to
    ///     the same compatibility standards as public APIs. It may be changed or removed without notice in
    ///     any release. You should only use it directly in your code with extreme caution and knowing that
    ///     doing so can result in application failures when updating to a new Entity Framework Core release.
    /// </summary>
    public virtual StoredProcedureResultColumn AddRowsAffectedResultColumn()
    {
        if (_rowsAffectedResultColumn != null
            || _rowsAffectedParameter != null
            || _isRowsAffectedReturned)
        {
            throw new InvalidOperationException(
                RelationalStrings.StoredProcedureDuplicateRowsAffectedResultColumn(
                    ((IReadOnlyStoredProcedure)this).GetStoreIdentifier()?.DisplayName()));
        }

        var resultColumn = new StoredProcedureResultColumn(this, forRowsAffected: true, propertyName: null);
        _resultColumns.Add(resultColumn);
        _rowsAffectedResultColumn = resultColumn;

        return resultColumn;
    }

    /// <summary>
    ///     This is an internal API that supports the Entity Framework Core infrastructure and not subject to
    ///     the same compatibility standards as public APIs. It may be changed or removed without notice in
    ///     any release. You should only use it directly in your code with extreme caution and knowing that
    ///     doing so can result in application failures when updating to a new Entity Framework Core release.
    /// </summary>
    public override string ToString()

View on GitHub (pinned to dbf9771522)

Solutions

  1. Pick one rows-affected mechanism and remove the others before adding the result column.
  2. If you want a result column instead of the return value, do not call ReturnsRowsAffected().
  3. Audit sproc builder calls so only one channel is configured.

Example fix

// before
var s = eb.InsertStoredProcedure();
s.ReturnsRowsAffected();
s.AddRowsAffectedResultColumn(); // throws

// after
var s = eb.InsertStoredProcedure();
s.AddRowsAffectedResultColumn();
Defensive patterns

Strategy: validation

Validate before calling

static StoredProcedureResultColumn AddRowsAffectedResultColumnOnce(StoredProcedure sproc)
{
    if (sproc.FindRowsAffectedResultColumn() is not null
        || sproc.FindRowsAffectedParameter() is not null
        || sproc.IsRowsAffectedReturned)
    {
        throw new InvalidOperationException("Rows affected already captured on this sproc.");
    }
    return sproc.AddRowsAffectedResultColumn();
}

Prevention

When it happens

Trigger: StoredProcedure.cs:649-656: _rowsAffectedResultColumn != null || _rowsAffectedParameter != null || _isRowsAffectedReturned. Produced by calling AddRowsAffectedResultColumn() after ReturnsRowsAffected() or AddRowsAffectedParameter().

Common situations: Mixing .ReturnsRowsAffected() with .RowsAffectedResultColumn(); scaffolding then re-stacking config; copying insert config to update without removing the return-value setting.

Related errors


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