Skip to content

Fix NotSupportedException when parsing nested object initialization#979

Merged
StefH merged 4 commits intomasterfrom
copilot/fix-nested-object-initialization
Mar 29, 2026
Merged

Fix NotSupportedException when parsing nested object initialization#979
StefH merged 4 commits intomasterfrom
copilot/fix-nested-object-initialization

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Mar 29, 2026

Parsing expressions with nested new initializers like new (new (3 as Id) as CurrentDepartment) throws a NotSupportedException when the result type is a known class (e.g. Customer).

Root cause: The parser propagates _resultType into nested new expressions that have no explicit type. So new (3 as Id) becomes new Customer() {Id=3}. When the outer expression then tries to bind this to CurrentDepartment (type Department), ExpressionPromoter.Promote returns null and Expression.Convert throws since Customer and Department are incompatible.

Fix: In CreateNewExpression's member-bindings path, when Promote returns null and the source expression is a MemberInitExpression, attempt to rebuild it for the target member type via a new TryRebuildMemberInitExpression helper. The helper:

  • Requires a default constructor on the target type
  • Remaps each binding by member name from source type to target type
  • Recurses for deeply nested MemberInitExpression values
  • Returns null (falls back to Expression.Convert) if any member is absent on the target

Example — previously threw, now works:

var lambda = DynamicExpressionParser.ParseLambda(
    ParsingConfig.DefaultEFCore21,
    typeof(Customer), typeof(Customer),
    "new (new (3 as Id) as CurrentDepartment)");

// Produces: Customer { CurrentDepartment = Department { Id = 3 } }

📱 Kick off Copilot coding agent tasks wherever you are with GitHub Mobile, available on iOS and Android.

Copilot AI linked an issue Mar 29, 2026 that may be closed by this pull request
Copilot AI changed the title [WIP] Fix error when parsing nested object initialization Fix NotSupportedException when parsing nested object initialization Mar 29, 2026
Copilot AI requested a review from StefH March 29, 2026 07:18
return Expression.MemberInit(Expression.New(type), memberBindings);
}

private static Expression? TryRebuildMemberInitExpression(MemberInitExpression memberInitExpression, Type targetType)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rewrite this method to:

private static bool TryRebuildMemberInitExpression(MemberInitExpression memberInitExpression, Type targetType, [NotNullWhen(true)] out Expression? expression)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 417258d. Rewrote the method signature to private static bool TryRebuildMemberInitExpression(MemberInitExpression memberInitExpression, Type targetType, [NotNullWhen(true)] out Expression? expression) and updated the call site accordingly.

Copilot AI requested a review from StefH March 29, 2026 14:34

// https://github.com/zzzprojects/System.Linq.Dynamic.Core/issues/701
[Fact]
public void DynamicExpressionParser_ParseLambda_NestedObjectInitialization()
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a unit test which goes 1 level deeper so that the code

if (bindingExpression is MemberInitExpression nestedMemberInit && bindingExpression.Type != targetMemberType)
{
    if (!TryRebuildMemberInitExpression(nestedMemberInit, targetMemberType, out var rebuiltNested))
    {
        return false;
    }
    bindingExpression = rebuiltNested;
}
else if (bindingExpression.Type != targetMemberType)
{
    bindingExpression = Expression.Convert(bindingExpression, targetMemberType);
}

is also tested

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added in ddb00b9. The new test DynamicExpressionParser_ParseLambda_NestedObjectInitialization_ThreeLevelsDeep uses expression new (new (new (3 as Id) as Sub) as Sub) with models that have a shared Sub property name at each level (CustomerForNestedNewTest.Sub → DepartmentForNestedNewTest, DepartmentForNestedNewTest.Sub → SubDepartmentForNestedNewTest). This causes the outer rebuild to encounter an inner MemberInitExpression binding, triggering the recursive call path.

Copilot AI requested a review from StefH March 29, 2026 16:47
@StefH StefH marked this pull request as ready for review March 29, 2026 17:19
@StefH StefH merged commit e33dc6c into master Mar 29, 2026
4 checks passed
@StefH StefH deleted the copilot/fix-nested-object-initialization branch March 29, 2026 17:20
@StefH StefH added the bug label Mar 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Development

Successfully merging this pull request may close these issues.

Error when parsing a nested object initialization

2 participants