dotnet/efcore · error · InvalidOperationException

IncompatibleComplexTypesInAssignment

IncompatibleComplexTypesInAssignment

Error message

The complex types '{complexType1}' and '{complexType2}' are being assigned, but the latter is lacking property '{property}' of the former.

What it means

When assigning one complex-type column to another (line 549-558), EF looks up a matching complex property by name on the value's complex type. If the value's complex type is missing a complex property that the target has (same CLR type but different model shape, e.g. ShippingAddress vs BillingAddress where only one has a County sub-object), IncompatibleComplexTypesInAssignment is thrown naming the missing property.

Source

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

                    {
                        throw new InvalidOperationException(
                            RelationalStrings.ExecuteUpdateOverJsonIsNotSupported(nestedTargetComplexProperty.ComplexType.DisplayName()));
                    }

                    var nestedTargetExpression =
                        (StructuralTypeShaperExpression)targetProjection.BindComplexProperty(nestedTargetComplexProperty);

                    // If the value expression is a shaper with its own complex type (as opposed to a constant/parameter), we're assigning
                    // one (modeled) column to another. In that case, find the corresponding property on the value complex type (which is
                    // different than the target complex type, despite the two having the same CLR type, e.g. compare ShippingAddress to
                    // BillingAddress).
                    // Otherwise, if the value expression is a constant/parameter, just use the target complex property.
                    var nestedValueComplexProperty = valueExpression is StructuralTypeShaperExpression
                    {
                        StructuralType: IComplexType valueNestedComplexType
                    }
                        ? valueNestedComplexType!.FindComplexProperty(nestedTargetComplexProperty.Name)
                        ?? throw new InvalidOperationException(
                            RelationalStrings.IncompatibleComplexTypesInAssignment(
                                targetNestedComplexType.DisplayName(), valueNestedComplexType.DisplayName(),
                                nestedTargetComplexProperty.Name))
                        : nestedTargetComplexProperty;

                    var nestedValueExpression = CreateComplexPropertyAccessExpression(valueExpression, nestedValueComplexProperty);

                    ProcessComplexType(nestedTargetExpression, nestedValueExpression);
                }

                Expression CreatePropertyAccessExpression(Expression target, IProperty property)
                {
                    return target is LambdaExpression lambda
                        ? Expression.Lambda(Core(lambda.Body, property), lambda.Parameters[0])
                        : Core(target, property);

                    Expression Core(Expression target, IProperty property)
                    {

View on GitHub (pinned to dbf9771522)

Solutions

  1. Configure both complex types identically so they have the same set of nested complex properties.
  2. Instead of assigning one complex column to another, assign scalar properties individually.
  3. Pass a constant/parameter of the CLR type rather than a cross-column shaper reference so the target's own structure is used.

Example fix

// before (ShippingAddress has nested Region that BillingAddress lacks)
db.Customers.ExecuteUpdate(s => s.SetProperty(
    c => c.BillingAddress, c => c.ShippingAddress));

// after (configure both complex types identically, or update scalars)
modelBuilder.Entity<Customer>()
    .ComplexProperty(c => c.BillingAddress, a => a.ComplexProperty(x => x.Region))
    .ComplexProperty(c => c.ShippingAddress, a => a.ComplexProperty(x => x.Region));
Defensive patterns

Strategy: validation

Validate before calling

// Ensure two complex types share the same nested complex property names before cross-assigning.
var targetNames = targetComplexType.GetComplexProperties().Select(p => p.Name);
var valueNames = valueComplexType.GetComplexProperties().Select(p => p.Name);
var missing = targetNames.Except(valueNames).ToList();
if (missing.Any()) throw new InvalidOperationException($"Value complex type missing: {string.Join(", ", missing)}");

Try / catch

try { await q.ExecuteUpdateAsync(s => s.SetProperty(e => e.Billing, e => e.Shipping)); }
catch (InvalidOperationException ex) when (ex.Message.Contains("lacking property"))
{ /* configure both complex types identically, or assign scalars individually */ }

Prevention

When it happens

Trigger: SetProperty assigning a value whose StructuralTypeShaperExpression complex type lacks a nested complex property present on the target, e.g. SetProperty(e => e.BillingAddress, e => e.ShippingAddress) where ShippingAddress has a nested complex property that BillingAddress does not.

Common situations: Two complex types sharing the same CLR type but configured differently in the model; partial configuration of one complex type (a property was configured on one but not the other); refactoring where a nested value object was added to one side only.

Related errors


AI-assisted analysis of dotnet/efcore@dbf9771522 (2026-08-06). Data as JSON: /api/errors/39dc92f86db5b084. Report an issue: GitHub.