{"record":{"id":"8dfa43911e3a8b03","repo":"dotnet/efcore","slug":"multiple-setproperty-invocations-refer-to-differ","errorCode":null,"errorMessage":"Multiple 'SetProperty' invocations refer to different tables ('{propertySelector1}' and '{propertySelector2}'). A single 'ExecuteUpdate' call can only update the columns of a single table.","messagePattern":"Multiple 'SetProperty' invocations refer to different tables \\('(.+?)' and '(.+?)'\\)\\. A single 'ExecuteUpdate' call can only update the columns of a single table\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.ExecuteUpdate.cs","lineNumber":781,"sourceCode":"                }\n\n                var result = _sqlTranslator.TranslateProjection(remappedValueSelector, applyDefaultTypeMapping: false);\n\n                return result is null\n                    ? throw new InvalidOperationException(RelationalStrings.InvalidValueInSetProperty(valueSelector.Print()))\n                    : result;\n            }\n\n            void CheckColumnOnSameTable(ColumnExpression column, LambdaExpression propertySelector)\n            {\n                if (targetTableAlias is null)\n                {\n                    targetTableAlias = column.TableAlias;\n                    targetTablePropertySelector = propertySelector;\n                }\n                else if (column.TableAlias != targetTableAlias)\n                {\n                    throw new InvalidOperationException(\n                        RelationalStrings.MultipleTablesInExecuteUpdate(propertySelector.Print(), targetTablePropertySelector!.Print()));\n                }\n            }\n\n            // If the entire JSON column is being referenced, remove the JsonQueryExpression altogether and just return\n            // the column (no need for special JSON modification functions/syntax).\n            // See #30768 for stopping producing empty Json{Scalar,Query}Expressions.\n            // Otherwise, convert the JsonQueryExpression to a JsonScalarExpression, which is our current representation for a complex\n            // JSON in the SQL tree (as opposed to in the shaper) - see #36392.\n            static SqlExpression ProcessJsonQuery(JsonQueryExpression jsonQuery)\n                => jsonQuery.Path is []\n                    ? jsonQuery.JsonColumn\n                    : new JsonScalarExpression(\n                        jsonQuery.JsonColumn,\n                        jsonQuery.Path,\n                        jsonQuery.Type,\n                        jsonQuery.JsonColumn.TypeMapping,\n                        jsonQuery.IsNullable);","sourceCodeStart":763,"sourceCodeEnd":799,"githubUrl":"https://github.com/dotnet/efcore/blob/3a2006ef569de08368d59db5e1468aa8f407e4f8/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.ExecuteUpdate.cs#L763-L799","documentation":"Thrown by CheckColumnOnSameTable when two SetProperty calls within one ExecuteUpdate resolve to columns on different tables. A single ExecuteUpdate can only modify columns of one table; mixing tables (e.g. via TPT inheritance or navigations) is rejected.","triggerScenarios":"Two SetProperty lambdas whose resolved ColumnExpressions have different TableAlias — e.g. SetProperty(o => o.OrderDate, ...) and SetProperty(o => o.Customer.ContactName, ...) (the latter lives on the Customers table). The TPT inheritance test (k => k.FoundOn vs k => k.Name) reproduces it.","commonSituations":"TPT inheritance where properties live on different mapped tables; updating a navigation's property in the same ExecuteUpdate as the root entity's property; combining owned/principal columns in one bulk update.","solutions":["Split into multiple ExecuteUpdate calls, one per table.","For TPT, ensure both properties belong to the same table or restructure to TPH if single-call updates are required.","Drop the SetProperty that targets a different table (often a navigation) from the combined call."],"exampleFix":"// before\nawait ctx.Set<Order>()\n    .ExecuteUpdateAsync(s => s\n        .SetProperty(o => o.OrderDate, DateTime.Now)\n        .SetProperty(o => o.Customer.ContactName, \"x\"));\n// after\nawait ctx.Set<Order>()\n    .ExecuteUpdateAsync(s => s.SetProperty(o => o.OrderDate, DateTime.Now));\nawait ctx.Set<Customer>()\n    .Where(c => c.Orders.Any())\n    .ExecuteUpdateAsync(s => s.SetProperty(c => c.ContactName, \"x\"));","handlingStrategy":"validation","validationCode":"// Group setters by table before issuing ExecuteUpdate.\n// Static analysis: ensure both properties map to the same table.\nIEntityType et = ctx.Model.FindEntityType(typeof(Order))!;\nvar p1 = et.FindProperty(nameof(Order.OrderDate))!;\n// Customer.ContactName lives on a different entity/table -> split the call.","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Issue one ExecuteUpdate per table; never mix navigations into the root's setter.","For TPT models, split updates across the table each property belongs to.","Add a test asserting a single ExecuteUpdate touches one table."],"tags":["efcore","executeupdate","tpt","inheritance","query-translation"],"backgroundTag":null,"analyzedSha":"3a2006ef569de08368d59db5e1468aa8f407e4f8","analyzedAt":"2026-08-11T23:42:04.146Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}