{"record":{"id":"cf324fc012c5c964","repo":"dotnet/efcore","slug":"the-entity-type-entitytype-is-mapped-to-the-st-cf324f","errorCode":null,"errorMessage":"The entity type '{entityType}' is mapped to the stored procedure '{sproc}' which returns both result columns and a rows affected value. If the stored procedure returns result columns, a rows affected value isn't needed and can be safely removed.","messagePattern":"The entity type '(.+?)' is mapped to the stored procedure '(.+?)' which returns both result columns and a rows affected value\\. If the stored procedure returns result columns, a rows affected value isn't needed and can be safely removed\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs","lineNumber":927,"sourceCode":"        if (sproc.IsRowsAffectedReturned\n            || sproc.FindRowsAffectedParameter() != null\n            || sproc.FindRowsAffectedResultColumn() != null)\n        {\n            if (storeObjectIdentifier.StoreObjectType == StoreObjectType.InsertStoredProcedure)\n            {\n                throw new InvalidOperationException(\n                    RelationalStrings.StoredProcedureRowsAffectedForInsert(\n                        storeObjectIdentifier.DisplayName()));\n            }\n\n            if (originalValueProperties.Values.FirstOrDefault(p => p.IsConcurrencyToken) is { } missedConcurrencyToken)\n            {\n                logger.StoredProcedureConcurrencyTokenNotMapped(entityType, missedConcurrencyToken, storeObjectIdentifier.DisplayName());\n            }\n\n            if (sproc.ResultColumns.Any(c => c != sproc.FindRowsAffectedResultColumn()))\n            {\n                throw new InvalidOperationException(\n                    RelationalStrings.StoredProcedureRowsAffectedWithResultColumns(\n                        entityType.DisplayName(),\n                        storeObjectIdentifier.DisplayName()));\n            }\n        }\n    }\n\n    /// <summary>\n    ///     Validates a <see cref=\"bool\" /> property with defaults.\n    /// </summary>\n    /// <param name=\"property\">The property to validate.</param>\n    /// <param name=\"logger\">The logger to use.</param>\n    protected virtual void ValidateBoolWithDefaults(\n        IProperty property,\n        IDiagnosticsLogger<DbLoggerCategory.Model.Validation> logger)\n    {\n        if (!property.ClrType.IsNullableType()\n            && (property.ClrType.IsEnum || property.ClrType == typeof(bool))","sourceCodeStart":909,"sourceCodeEnd":945,"githubUrl":"https://github.com/dotnet/efcore/blob/dbf9771522148d61a2467854921bd5dc6f6e6916/src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs#L909-L945","documentation":"An Update/Delete stored procedure can signal optimistic-concurrency failure either by returning zero rows-affected OR by returning a result set whose columns re-select the current row values for EF to compare. Using both is redundant and ambiguous. The validator at line 925-931 throws when the sproc has result columns beyond the rows-affected result column AND any rows-affected signal is configured.","triggerScenarios":"On an Update/Delete sproc: simultaneously calling `.ResultColumn(...)` for re-selected columns AND `.RowsAffectedParameter(...)` / `.RowsAffectedResultColumn(...)` / `.ReturnsRowsAffected()`.","commonSituations":"Copy-pasting a sproc template that returns both a row count and the full row; migrating from a rows-affected model to a result-column model but forgetting to remove the old config.","solutions":["If the sproc returns result columns, remove all rows-affected configuration (.RowsAffectedParameter/.RowsAffectedResultColumn/.ReturnsRowsAffected).","Alternatively, remove the .ResultColumn mappings and keep only the rows-affected signal if the sproc does not re-select row data.","Align the database stored procedure to use one strategy consistently."],"exampleFix":"// before\nmodelBuilder.Entity<Order>()\n    .UpdateStoredProcedure(o => o\n        .RowsAffectedParameter(\"rc\")\n        .ResultColumn(p => p.Amount, \"amt\")\n        .ResultColumn(p => p.Status, \"status\"));\n\n// after - keep result columns, drop rows-affected\nmodelBuilder.Entity<Order>()\n    .UpdateStoredProcedure(o => o\n        .ResultColumn(p => p.Amount, \"amt\")\n        .ResultColumn(p => p.Status, \"status\"));","handlingStrategy":"validation","validationCode":"foreach (var et in modelBuilder.Model.GetEntityTypes())\n{\n    foreach (var sproc in et.GetStoredProcedures())\n    {\n        bool hasRowsAffected = sproc.IsRowsAffectedReturned\n            || sproc.FindRowsAffectedParameter() != null\n            || sproc.FindRowsAffectedResultColumn() != null;\n        bool hasResultColumns = sproc.ResultColumns\n            .Any(c => c != sproc.FindRowsAffectedResultColumn());\n        if (hasRowsAffected && hasResultColumns)\n        {\n            throw new InvalidOperationException(\n                $\"{et.Name} sproc combines rows-affected with result columns.\");\n        }\n    }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Pick one concurrency strategy per Update/Delete sproc: rows-affected XOR result columns.","When migrating from rows-affected to result columns, delete the old config in the same commit.","Document the chosen strategy in the sproc builder helper so it is applied consistently."],"tags":["ef-core","stored-procedures","model-validation","concurrency"],"analyzedSha":"dbf9771522148d61a2467854921bd5dc6f6e6916","analyzedAt":"2026-08-06T20:46:03.226Z","schemaVersion":2},"datasetVersion":"2026-08-07T03:17:09.362Z"}