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

  1. Ensure ExecuteUpdate receives a straightforward chain: .ExecuteUpdate(s => s.SetProperty(...).SetProperty(...)).
  2. Do not wrap SetProperty calls inside other method calls or conditional expressions in the setters lambda.
  3. 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

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


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)