{"record":{"id":"2e3fcb2ead9a282f","repo":"dotnet/efcore","slug":"multipletablesinexecuteupdate","errorCode":"MultipleTablesInExecuteUpdate","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/dbf9771522148d61a2467854921bd5dc6f6e6916/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.ExecuteUpdate.cs#L763-L799","documentation":"CheckColumnOnSameTable (line 772-784) records the table alias of the first setter's column and verifies every subsequent setter's column lives on the same table. A single UPDATE statement can only target one table, so if two SetProperty calls resolve to columns on different tables, MultipleTablesInExecuteUpdate is thrown, naming both offending selectors.","triggerScenarios":"Chaining SetProperty calls whose properties map to columns on different tables: e.g. SetProperty(e => e.Name, ...).SetProperty(e => e.Profile.Bio, ...) where Profile is a table-split or owned entity on a separate table, or two entities sharing a table via splitting where EF chose different tables.","commonSituations":"Table splitting where the dependent lives on a different table; owned entities mapped to their own tables; TPT/TPC inheritance where properties map to different hierarchy tables; attempting a multi-table update in one ExecuteUpdate call.","solutions":["Restrict a single ExecuteUpdate to properties of one table; issue separate ExecuteUpdate calls per table.","Map the dependent/owned entity to the same table (table splitting) so all columns share one alias.","Reorder so each ExecuteUpdate call's setters are all on the principal table."],"exampleFix":"// before (User and Profile live on different tables)\ndb.Users.ExecuteUpdate(s => s\n    .SetProperty(u => u.Name, \"x\")\n    .SetProperty(u => u.Profile.Bio, \"y\"));\n\n// after (one ExecuteUpdate per table)\ndb.Users.ExecuteUpdate(s => s.SetProperty(u => u.Name, \"x\"));\ndb.Profiles.ExecuteUpdate(s => s.SetProperty(p => p.Bio, \"y\"));","handlingStrategy":"validation","validationCode":"// Verify all setter target properties map to the same table before calling ExecuteUpdate.\nvar tables = setters.Select(s => GetTableFor(s.Property)).Distinct();\nif (tables.Count() > 1) throw new InvalidOperationException(\"Setters target multiple tables; split into separate ExecuteUpdate calls.\");","typeGuard":null,"tryCatchPattern":"try { await q.ExecuteUpdateAsync(s => s.SetProperty(e => e.A, 1).SetProperty(e => e.Nav.B, 2)); }\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"different tables\"))\n{ /* issue one ExecuteUpdate per table */ }","preventionTips":["Keep each ExecuteUpdate scoped to one table's columns.","Use table splitting (ToTable same name) for owned entities updated together.","Split multi-table updates into sequential ExecuteUpdate calls."],"tags":["efcore","executeupdate","table-splitting","multi-table"],"analyzedSha":"dbf9771522148d61a2467854921bd5dc6f6e6916","analyzedAt":"2026-08-06T20:46:03.226Z","schemaVersion":2},"datasetVersion":"2026-08-07T02:17:10.218Z"}