{"record":{"id":"80e1b14931572b71","repo":"dotnet/efcore","slug":"the-rows-affected-result-column-cannot-be-added-to","errorCode":null,"errorMessage":"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.","messagePattern":"The rows affected result column cannot be added to the stored procedure '(.+?)' because the rows affected are already returned via another column, via a parameter or via the stored procedure return value\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/EFCore.Relational/Metadata/Internal/StoredProcedure.cs","lineNumber":653,"sourceCode":"    ///     any release. You should only use it directly in your code with extreme caution and knowing that\n    ///     doing so can result in application failures when updating to a new Entity Framework Core release.\n    /// </summary>\n    public virtual StoredProcedureResultColumn? FindRowsAffectedResultColumn()\n        => _rowsAffectedResultColumn;\n\n    /// <summary>\n    ///     This is an internal API that supports the Entity Framework Core infrastructure and not subject to\n    ///     the same compatibility standards as public APIs. It may be changed or removed without notice in\n    ///     any release. You should only use it directly in your code with extreme caution and knowing that\n    ///     doing so can result in application failures when updating to a new Entity Framework Core release.\n    /// </summary>\n    public virtual StoredProcedureResultColumn AddRowsAffectedResultColumn()\n    {\n        if (_rowsAffectedResultColumn != null\n            || _rowsAffectedParameter != null\n            || _isRowsAffectedReturned)\n        {\n            throw new InvalidOperationException(\n                RelationalStrings.StoredProcedureDuplicateRowsAffectedResultColumn(\n                    ((IReadOnlyStoredProcedure)this).GetStoreIdentifier()?.DisplayName()));\n        }\n\n        var resultColumn = new StoredProcedureResultColumn(this, forRowsAffected: true, propertyName: null);\n        _resultColumns.Add(resultColumn);\n        _rowsAffectedResultColumn = resultColumn;\n\n        return resultColumn;\n    }\n\n    /// <summary>\n    ///     This is an internal API that supports the Entity Framework Core infrastructure and not subject to\n    ///     the same compatibility standards as public APIs. It may be changed or removed without notice in\n    ///     any release. You should only use it directly in your code with extreme caution and knowing that\n    ///     doing so can result in application failures when updating to a new Entity Framework Core release.\n    /// </summary>\n    public override string ToString()","sourceCodeStart":635,"sourceCodeEnd":671,"githubUrl":"https://github.com/dotnet/efcore/blob/3a2006ef569de08368d59db5e1468aa8f407e4f8/src/EFCore.Relational/Metadata/Internal/StoredProcedure.cs#L635-L671","documentation":"The rows-affected count may be returned through only one channel. AddRowsAffectedResultColumn() throws when rows affected is already carried by an existing result column, a parameter, or the stored procedure's return value. This mirrors AddRowsAffectedParameter() but guards the result-column channel.","triggerScenarios":"Calling .RowsAffectedResultColumn() after .RowsAffectedParameter(), .RowsAffectedResultColumn(), or .RowsAffectedReturned() was already applied to the same stored procedure.","commonSituations":"Switching a procedure's rows-affected mechanism (e.g. from an output parameter to a SELECT result set) without removing the prior configuration; copy-pasting fluent blocks across Insert/Update/Delete mappings; conventions from a prior EF version leaving a residual channel after an upgrade.","solutions":["Check FindRowsAffectedResultColumn(), FindRowsAffectedParameter(), and IsRowsAffectedReturned before adding a result column.","Pick exactly one rows-affected channel and remove the others.","If the procedure SELECTs rows affected, use .RowsAffectedResultColumn() alone.","Rebuild the procedure mapping from scratch if the source of the duplicate channel is unclear."],"exampleFix":"// before\nsp.RowsAffectedParameter();\nsp.RowsAffectedResultColumn(); // throws\n\n// after\nsp.RowsAffectedResultColumn();","handlingStrategy":"validation","validationCode":"var sp = entityType.GetUpdateStoredProcedure();\nif (sp is not null\n    && sp.FindRowsAffectedResultColumn() is null\n    && sp.FindRowsAffectedParameter() is null\n    && !sp.IsRowsAffectedReturned)\n{\n    sp.RowsAffectedResultColumn();\n}","typeGuard":"static bool CanAddRowsAffectedResultColumn(IStoredProcedure sp)\n    => sp.FindRowsAffectedResultColumn() is null\n       && sp.FindRowsAffectedParameter() is null\n       && !sp.IsRowsAffectedReturned;","tryCatchPattern":null,"preventionTips":["Pick one rows-affected channel per procedure and never mix.","Encapsulate the choice in a shared helper invoked for Insert/Update/Delete.","Validate the model in tests to catch duplicate channels before runtime."],"tags":["ef-core","stored-procedure","configuration","rows-affected","model-building"],"backgroundTag":null,"analyzedSha":"3a2006ef569de08368d59db5e1468aa8f407e4f8","analyzedAt":"2026-08-11T23:42:04.146Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}