dotnet/efcore · error · InvalidOperationException
The 'setPropertyCalls' argument to 'ExecuteUpdate' may only…
Error message
The 'setPropertyCalls' argument to 'ExecuteUpdate' may only contain a chain of 'SetProperty' expressing the properties to be updated.
What it means
Thrown by ProcessExecuteUpdate while walking the expression tree of an ExecuteUpdate call's setPropertyCalls argument. It expects a nested chain of SetProperty(...) calls on an UpdateSettersBuilder<T>; if the current node does not match that shape the loop aborts and throws InvalidOperationException with RelationalStrings.InvalidArgumentToExecuteUpdate.
Solutions
- Ensure ExecuteUpdate receives a straightforward chain: .ExecuteUpdate(s => s.SetProperty(...).SetProperty(...)).
- Do not wrap SetProperty calls inside other method calls or conditional expressions in the setters lambda.
- If building the expression programmatically, replicate the exact UpdateSettersBuilder<T>.SetProperty chain shape.
Example fix
// before .ExecuteUpdate(s => SomeWrapper(s.SetProperty(p => p.Name, v))) // after .ExecuteUpdate(s => s.SetProperty(p => p.Name, v))
Defensive patterns
Strategy: validation
Validate before calling
// Validate ExecuteUpdate setters are a pure SetProperty chain
static bool IsValidExecuteUpdateChain(LambdaExpression setters)
{
var param = setters.Parameters.Single();
var node = setters.Body;
while (node != param)
{
if (node is not MethodCallExpression m
|| m.Method.Name != nameof(UpdateSettersBuilder<int>.SetProperty)) return false;
node = m.Object;
}
return true;
} Try / catch
try { generator.Generate(/*...*/); }
catch (InvalidOperationException ex) when (ex.Message.Contains("ExecuteUpdate"))
{ throw new InvalidOperationException("ExecuteUpdate setters must be a chain of SetProperty calls.", ex); } Prevention
- Write ExecuteUpdate setters as a flat chain: .SetProperty(...).SetProperty(...).
- Do not wrap SetProperty in other method calls or conditionals.
- When building the expression programmatically, mirror the exact UpdateSettersBuilder<T> shape.
When it happens
Trigger: A precompiled query contains an ExecuteUpdate whose setters lambda is not a pure chain of UpdateSettersBuilder<T>.SetProperty(...) calls (e.g. includes arbitrary method calls, property reads, or a different structure).
Common situations: Building the SetProperty argument dynamically/programmatically with nodes the parser does not recognise; passing a lambda that wraps SetProperty calls inside other expressions; version mismatch where the setter shape changed.
Related errors
- Encountered non-quotable expression of type
- Missing default arm for switch expression
- Switch with non-null comparison method
- Unary node with non-null method
- Unnamed captured variable
AI-assisted analysis of dotnet/efcore@3a2006ef56 (2026-08-11).
Data as JSON: /api/errors/88f8f9abf2dd94df.
Report an issue: GitHub.
Appendix: source
Thrown at src/EFCore.Design/Query/Internal/PrecompiledQueryCodeGenerator.cs:1250
{
if (valueSelector is UnaryExpression
{
NodeType: ExpressionType.Quote,
Operand: LambdaExpression unwrappedValueSelector
})
{
settersBuilder.SetProperty(propertySelector, unwrappedValueSelector);
}
else
{
settersBuilder.SetProperty(propertySelector, valueSelector);
}
expression = methodCallExpression.Object;
continue;
}
throw new InvalidOperationException(RelationalStrings.InvalidArgumentToExecuteUpdate);
}
// The expression tree is nested inside-out (last SetProperty call is the outermost node),
// so setters were added in reverse order. Reverse to restore source code order.
var settersArray = settersBuilder.BuildSettersExpression();
return Expression.NewArrayInit(settersArray.Type.GetElementType()!, settersArray.Expressions.Reverse());
}
private static ITypeSymbol GetTypeSymbol(Compilation compilation, Type type)
{
if (type.IsByRef || type.IsPointer || type.IsGenericParameter || type.IsByRefLike)
{
throw new NotSupportedException($"Unsupported type: {type}");
}
if (type.IsArray)
{
var elementSymbol = GetTypeSymbol(compilation, type.GetElementType()!);View on GitHub (pinned to 3a2006ef56)