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 in PrecompiledQueryCodeGenerator.ProcessExecuteUpdate (line 1250, using RelationalStrings.InvalidArgumentToExecuteUpdate) while precompiling an ExecuteUpdate call. EF walks the lambda passed to ExecuteUpdate expecting an inside-out chain of UpdateSettersBuilder<T>.SetProperty(...) calls; as soon as it encounters a node that is not such a SetProperty call it throws. The 'setPropertyCalls' argument may only express the properties to update via SetProperty.

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 dbf9771522)

Solutions

  1. Ensure the ExecuteUpdate setters lambda is strictly a chain of s.SetProperty(x => x.Prop, value) calls with no other operations in between.
  2. Move any computation out of the setters lambda: compute the value in a variable before ExecuteUpdate and reference it inside SetProperty.
  3. If you need SetProperty overloads, use the property-selector + value-selector forms that EF's UpdateSettersBuilder provides.
  4. Exclude the ExecuteUpdate query from precompilation if it cannot be reduced to a pure SetProperty chain.

Example fix

// before: non-SetProperty node inside the setters chain
await db.Blogs.Where(b => b.Id == id)
    .ExecuteUpdateAsync(s => s.SetProperty(b => b.Updated, DateTime.Now).AlsoDoSomething());

// after: pure SetProperty chain, value computed beforehand
var now = DateTime.Now;
await db.Blogs.Where(b => b.Id == id)
    .ExecuteUpdateAsync(s => s.SetProperty(b => b.Updated, now));
Defensive patterns

Strategy: validation

Validate before calling

// Validate the ExecuteUpdate setters lambda is a pure SetProperty chain before precompiling
static bool IsPureSetPropertyChain(LambdaExpression lambda)
{
    var param = lambda.Parameters.Single();
    var body = lambda.Body;
    while (body != param)
    {
        if (body is MethodCallExpression mc
            && mc.Method.Name == nameof(UpdateSettersBuilder<int>.SetProperty))
            body = mc.Object;
        else return false;
    }
    return true;
}

Try / catch

try { /* precompile ExecuteUpdate query */ }
catch (InvalidOperationException ex) when (ex.Message.Contains("ExecuteUpdate"))
{ /* move computations out of the setters; rebuild as a pure SetProperty chain */ }

Prevention

When it happens

Trigger: Precompiling a query whose ExecuteUpdate setters lambda contains anything other than a chain of SetProperty calls (e.g. an inline computation, a nested method call, an assignment, or a SetProperty variant with a different signature/declaring type). The while loop walking expression != settersParameter fails to match the SetProperty pattern and falls through to the throw.

Common situations: Writing ExecuteUpdate(s => s.SetProperty(x => x.Foo, expr).SetProperty(...)) where expr or an intermediate node is not a quoted SetProperty call. Mixing other method calls into the setters chain. Using a custom SetProperty-like helper instead of EF's SetProperty. A malformed expression tree produced by an older/different EF version.

Related errors


AI-assisted analysis of dotnet/efcore@dbf9771522 (2026-08-06). Data as JSON: /api/errors/88f8f9abf2dd94df. Report an issue: GitHub.