From 8914dd9da30f07a47add773127e2ba2715f80697 Mon Sep 17 00:00:00 2001 From: Serhii Kozachenko Date: Mon, 8 Dec 2025 22:48:50 +0000 Subject: [PATCH 1/2] Reverse processing order of setters in query generator Refactor processing of method call expressions in setters. --- .../Internal/PrecompiledQueryCodeGenerator.cs | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/EFCore.Design/Query/Internal/PrecompiledQueryCodeGenerator.cs b/src/EFCore.Design/Query/Internal/PrecompiledQueryCodeGenerator.cs index 954e7218fea..69bb8ce8c44 100644 --- a/src/EFCore.Design/Query/Internal/PrecompiledQueryCodeGenerator.cs +++ b/src/EFCore.Design/Query/Internal/PrecompiledQueryCodeGenerator.cs @@ -1170,6 +1170,11 @@ private static NewArrayExpression ProcessExecuteUpdate(MethodCallExpression exec var settersParameter = settersLambda.Parameters.Single(); var expression = settersLambda.Body; + // Use a Stack to reverse the order of processing. + // The expression tree is nested (Last Call is the Outer-most node). + // We want to process them First -> Last. + var calls = new Stack(); + while (expression != settersParameter) { if (expression is MethodCallExpression @@ -1200,6 +1205,8 @@ private static NewArrayExpression ProcessExecuteUpdate(MethodCallExpression exec settersBuilder.SetProperty(propertySelector, valueSelector); } + // Push to stack to process later + calls.Push(methodCallExpression); expression = methodCallExpression.Object; continue; } @@ -1207,6 +1214,27 @@ private static NewArrayExpression ProcessExecuteUpdate(MethodCallExpression exec throw new InvalidOperationException(RelationalStrings.InvalidArgumentToExecuteUpdate); } + // Process the stack (First-In, Last-Out) effectively reversing the tree traversal + // so setters are added in the order they were written in source code. + foreach (var methodCallExpression in calls) + { + var propertySelector = (LambdaExpression)((UnaryExpression)methodCallExpression.Arguments[0]).Operand; + var valueSelector = methodCallExpression.Arguments[1]; + + if (valueSelector is UnaryExpression + { + NodeType: ExpressionType.Quote, + Operand: LambdaExpression unwrappedValueSelector + }) + { + settersBuilder.SetProperty(propertySelector, unwrappedValueSelector); + } + else + { + settersBuilder.SetProperty(propertySelector, valueSelector); + } + } + return settersBuilder.BuildSettersExpression(); } From 7fcc86427d18869e74e8eb98d43cd9a08e7bd535 Mon Sep 17 00:00:00 2001 From: Serhii Kozachenko Date: Mon, 8 Dec 2025 22:59:38 +0000 Subject: [PATCH 2/2] Fix double add bug in ProcessExecuteUpdate Remove incorrect double-addition of properties during tree traversal --- .../Query/Internal/PrecompiledQueryCodeGenerator.cs | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/EFCore.Design/Query/Internal/PrecompiledQueryCodeGenerator.cs b/src/EFCore.Design/Query/Internal/PrecompiledQueryCodeGenerator.cs index 69bb8ce8c44..72b3614884f 100644 --- a/src/EFCore.Design/Query/Internal/PrecompiledQueryCodeGenerator.cs +++ b/src/EFCore.Design/Query/Internal/PrecompiledQueryCodeGenerator.cs @@ -1192,19 +1192,6 @@ private static NewArrayExpression ProcessExecuteUpdate(MethodCallExpression exec } methodCallExpression && methodCallExpression.Method.DeclaringType.GetGenericTypeDefinition() == typeof(UpdateSettersBuilder<>)) { - if (valueSelector is UnaryExpression - { - NodeType: ExpressionType.Quote, - Operand: LambdaExpression unwrappedValueSelector - }) - { - settersBuilder.SetProperty(propertySelector, unwrappedValueSelector); - } - else - { - settersBuilder.SetProperty(propertySelector, valueSelector); - } - // Push to stack to process later calls.Push(methodCallExpression); expression = methodCallExpression.Object;