dotnet/efcore · error · InvalidOperationException

Multiple 'SetProperty' invocations refer to different tables

Error message

Multiple 'SetProperty' invocations refer to different tables ('{propertySelector1}' and '{propertySelector2}'). A single 'ExecuteUpdate' call can only update the columns of a single table.

What it means

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.

Source

Thrown at src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.ExecuteUpdate.cs:781

                }

                var result = _sqlTranslator.TranslateProjection(remappedValueSelector, applyDefaultTypeMapping: false);

                return result is null
                    ? throw new InvalidOperationException(RelationalStrings.InvalidValueInSetProperty(valueSelector.Print()))
                    : result;
            }

            void CheckColumnOnSameTable(ColumnExpression column, LambdaExpression propertySelector)
            {
                if (targetTableAlias is null)
                {
                    targetTableAlias = column.TableAlias;
                    targetTablePropertySelector = propertySelector;
                }
                else if (column.TableAlias != targetTableAlias)
                {
                    throw new InvalidOperationException(
                        RelationalStrings.MultipleTablesInExecuteUpdate(propertySelector.Print(), targetTablePropertySelector!.Print()));
                }
            }

            // If the entire JSON column is being referenced, remove the JsonQueryExpression altogether and just return
            // the column (no need for special JSON modification functions/syntax).
            // See #30768 for stopping producing empty Json{Scalar,Query}Expressions.
            // Otherwise, convert the JsonQueryExpression to a JsonScalarExpression, which is our current representation for a complex
            // JSON in the SQL tree (as opposed to in the shaper) - see #36392.
            static SqlExpression ProcessJsonQuery(JsonQueryExpression jsonQuery)
                => jsonQuery.Path is []
                    ? jsonQuery.JsonColumn
                    : new JsonScalarExpression(
                        jsonQuery.JsonColumn,
                        jsonQuery.Path,
                        jsonQuery.Type,
                        jsonQuery.JsonColumn.TypeMapping,
                        jsonQuery.IsNullable);

View on GitHub (pinned to 3a2006ef56)

Solutions

  1. Split into multiple ExecuteUpdate calls, one per table.
  2. For TPT, ensure both properties belong to the same table or restructure to TPH if single-call updates are required.
  3. Drop the SetProperty that targets a different table (often a navigation) from the combined call.

Example fix

// before
await ctx.Set<Order>()
    .ExecuteUpdateAsync(s => s
        .SetProperty(o => o.OrderDate, DateTime.Now)
        .SetProperty(o => o.Customer.ContactName, "x"));
// after
await ctx.Set<Order>()
    .ExecuteUpdateAsync(s => s.SetProperty(o => o.OrderDate, DateTime.Now));
await ctx.Set<Customer>()
    .Where(c => c.Orders.Any())
    .ExecuteUpdateAsync(s => s.SetProperty(c => c.ContactName, "x"));
Defensive patterns

Strategy: validation

Validate before calling

// Group setters by table before issuing ExecuteUpdate.
// Static analysis: ensure both properties map to the same table.
IEntityType et = ctx.Model.FindEntityType(typeof(Order))!;
var p1 = et.FindProperty(nameof(Order.OrderDate))!;
// Customer.ContactName lives on a different entity/table -> split the call.

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of dotnet/efcore@3a2006ef56 (2026-08-11). Data as JSON: /api/errors/8dfa43911e3a8b03. Report an issue: GitHub.