{"record":{"id":"88f8f9abf2dd94df","repo":"dotnet/efcore","slug":"the-setpropertycalls-argument-to-executeupdate","errorCode":null,"errorMessage":"The 'setPropertyCalls' argument to 'ExecuteUpdate' may only contain a chain of 'SetProperty' expressing the properties to be updated.","messagePattern":"The 'setPropertyCalls' argument to 'ExecuteUpdate' may only contain a chain of 'SetProperty' expressing the properties to be updated\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/EFCore.Design/Query/Internal/PrecompiledQueryCodeGenerator.cs","lineNumber":1250,"sourceCode":"            {\n                if (valueSelector is UnaryExpression\n                    {\n                        NodeType: ExpressionType.Quote,\n                        Operand: LambdaExpression unwrappedValueSelector\n                    })\n                {\n                    settersBuilder.SetProperty(propertySelector, unwrappedValueSelector);\n                }\n                else\n                {\n                    settersBuilder.SetProperty(propertySelector, valueSelector);\n                }\n\n                expression = methodCallExpression.Object;\n                continue;\n            }\n\n            throw new InvalidOperationException(RelationalStrings.InvalidArgumentToExecuteUpdate);\n        }\n\n        // The expression tree is nested inside-out (last SetProperty call is the outermost node),\n        // so setters were added in reverse order. Reverse to restore source code order.\n        var settersArray = settersBuilder.BuildSettersExpression();\n        return Expression.NewArrayInit(settersArray.Type.GetElementType()!, settersArray.Expressions.Reverse());\n    }\n\n    private static ITypeSymbol GetTypeSymbol(Compilation compilation, Type type)\n    {\n        if (type.IsByRef || type.IsPointer || type.IsGenericParameter || type.IsByRefLike)\n        {\n            throw new NotSupportedException($\"Unsupported type: {type}\");\n        }\n\n        if (type.IsArray)\n        {\n            var elementSymbol = GetTypeSymbol(compilation, type.GetElementType()!);","sourceCodeStart":1232,"sourceCodeEnd":1268,"githubUrl":"https://github.com/dotnet/efcore/blob/dbf9771522148d61a2467854921bd5dc6f6e6916/src/EFCore.Design/Query/Internal/PrecompiledQueryCodeGenerator.cs#L1232-L1268","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Ensure the ExecuteUpdate setters lambda is strictly a chain of s.SetProperty(x => x.Prop, value) calls with no other operations in between.","Move any computation out of the setters lambda: compute the value in a variable before ExecuteUpdate and reference it inside SetProperty.","If you need SetProperty overloads, use the property-selector + value-selector forms that EF's UpdateSettersBuilder provides.","Exclude the ExecuteUpdate query from precompilation if it cannot be reduced to a pure SetProperty chain."],"exampleFix":"// before: non-SetProperty node inside the setters chain\nawait db.Blogs.Where(b => b.Id == id)\n    .ExecuteUpdateAsync(s => s.SetProperty(b => b.Updated, DateTime.Now).AlsoDoSomething());\n\n// after: pure SetProperty chain, value computed beforehand\nvar now = DateTime.Now;\nawait db.Blogs.Where(b => b.Id == id)\n    .ExecuteUpdateAsync(s => s.SetProperty(b => b.Updated, now));","handlingStrategy":"validation","validationCode":"// Validate the ExecuteUpdate setters lambda is a pure SetProperty chain before precompiling\nstatic bool IsPureSetPropertyChain(LambdaExpression lambda)\n{\n    var param = lambda.Parameters.Single();\n    var body = lambda.Body;\n    while (body != param)\n    {\n        if (body is MethodCallExpression mc\n            && mc.Method.Name == nameof(UpdateSettersBuilder<int>.SetProperty))\n            body = mc.Object;\n        else return false;\n    }\n    return true;\n}","typeGuard":null,"tryCatchPattern":"try { /* precompile ExecuteUpdate query */ }\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"ExecuteUpdate\"))\n{ /* move computations out of the setters; rebuild as a pure SetProperty chain */ }","preventionTips":["Write ExecuteUpdate setters as a pure chain of SetProperty calls only.","Compute values in variables before ExecuteUpdate.","Do not interleave other method calls in the setters lambda."],"tags":["ef-core","precompiled-query","executeupdate","design-time"],"analyzedSha":"dbf9771522148d61a2467854921bd5dc6f6e6916","analyzedAt":"2026-08-06T20:46:03.226Z","schemaVersion":2},"datasetVersion":"2026-08-07T03:17:09.362Z"}