dotnet/efcore · error · InvalidOperationException
The entity type ' ' is mapped to the stored procedure ' '…
Error message
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. What it means
Thrown when an UPDATE or DELETE sproc mapping returns both meaningful result columns (other than the rows-affected result column) AND a rows-affected value. EF reads entity data back through result columns; in that case a separate rows-affected value is redundant and confusing, so the validator rejects the combination. Only the if-branch where sproc.ResultColumns.Any(c => c != sproc.FindRowsAffectedResultColumn()) trips this.
Solutions
- Remove the rows-affected configuration (.RowsAffectedReturn(), .RowsAffectedResultColumn, .RowsAffectedParameter) since the result columns already imply success and provide data.
- Alternatively, remove the result columns if you genuinely want only rows-affected semantics — but EF cannot then read back store-generated values.
Example fix
// before
entityType.UpdateUsingStoredProcedure("UpdateOrder",
sp =>
{
sp.HasParameter(o => o.OrderId);
sp.HasResultColumn(o => o.UpdatedAt);
sp.RowsAffectedReturn(); // redundant
});
// after
entityType.UpdateUsingStoredProcedure("UpdateOrder",
sp =>
{
sp.HasParameter(o => o.OrderId);
sp.HasResultColumn(o => o.UpdatedAt);
}); Defensive patterns
Strategy: validation
Validate before calling
foreach (var et in model.GetEntityTypes())
{
foreach (var sproc in new[] { et.GetUpdateStoredProcedure(), et.GetDeleteStoredProcedure() })
{
if (sproc == null) continue;
bool hasRowsAffected = sproc.IsRowsAffectedReturned
|| sproc.FindRowsAffectedParameter() != null
|| sproc.FindRowsAffectedResultColumn() != null;
bool hasResultColumns = sproc.ResultColumns.Any(c => c != sproc.FindRowsAffectedResultColumn());
if (hasRowsAffected && hasResultColumns)
Console.WriteLine($"{sproc.Name} has both result columns and rows-affected; remove one.");
}
} Prevention
- Choose per sproc: result-column-based read-back or rows-affected-based — never both.
- When migrating from rows-affected to result columns, remove RowsAffectedReturn() / RowsAffectedResultColumn() in the same commit.
- Document each sproc's read-back strategy.
When it happens
Trigger: UpdateUsingStoredProcedure or DeleteUsingStoredProcedure has both .HasResultColumn(p => p.SomeProp) (a property result column) and a .RowsAffectedReturn() / .RowsAffectedResultColumn(...) / .RowsAffectedParameter(...) configured together.
Common situations: Migrating from a rows-affected sproc to a result-set-based sproc without removing the rows-affected call; adding a result column for an updated property while keeping the legacy rows-affected return.
Related errors
- A rows affected parameter, result column or return value…
- Both ' ' and ' ' are explicitly mapped to the stored…
- Both ' ' and ' ' are mapped to the stored procedure ' '…
- ' ' is mapped to the stored procedure ' ' while ' ' is…
- Input parameter ' ' of update stored procedure ' ' is…
AI-assisted analysis of dotnet/efcore@3a2006ef56 (2026-08-11).
Data as JSON: /api/errors/cf324fc012c5c964.
Report an issue: GitHub.
Appendix: source
Thrown at src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs:927
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()));
}
}
}
/// <summary>
/// Validates a <see cref="bool" /> property with defaults.
/// </summary>
/// <param name="property">The property to validate.</param>
/// <param name="logger">The logger to use.</param>
protected virtual void ValidateBoolWithDefaults(
IProperty property,
IDiagnosticsLogger<DbLoggerCategory.Model.Validation> logger)
{
if (!property.ClrType.IsNullableType()
&& (property.ClrType.IsEnum || property.ClrType == typeof(bool))View on GitHub (pinned to 3a2006ef56)