Skip to content

NewExpression constructor translations (e.g. new DateTimeOffset) don't work in projections (Select) #38083

@roji

Description

@roji

Summary

Constructor translations in the SQL translating expression visitor (e.g. new DateTimeOffset(dateTime, offset)TODATETIMEOFFSET(datetime, offset)) do not work inside Select() projections. They only work in Where(), OrderBy(), GroupBy(), etc.

This is because RelationalProjectionBindingExpressionVisitor.Visit() routes NewExpression directly to base.Visit()VisitNew(), which performs client-side DTO/anonymous type construction, rather than routing it through TranslateProjection() which would invoke the SQL translator's VisitNew override.

Minimal repro

// This WORKS (Where):
var results = await ctx.BasicTypes
    .Where(b => new DateTimeOffset(b.DateTime, new TimeSpan(2, 0, 0)) == someValue)
    .ToListAsync();

// This DOES NOT WORK (Select) - the constructor is evaluated client-side instead of being translated:
var results = await ctx.BasicTypes
    .Select(b => new DateTimeOffset(b.DateTime, new TimeSpan(2, 0, 0)))
    .ToListAsync();

Details

In RelationalProjectionBindingExpressionVisitor.Visit(), NewExpression is always routed to base.Visit() which calls VisitNew() for client-side construction:

case NewExpression or MemberInitExpression or StructuralTypeShaperExpression or IncludeExpression:
    return base.Visit(expression);

To support constructor translation in projections, NewExpression in index-based binding mode needs to be routed through TranslateProjection() (similar to how other expressions are handled). However, this must be done carefully to avoid breaking existing scenarios like new List<int> { ... } collection initializers, which use ListInitExpression containing a NewExpression.

A naive fix of routing all NewExpression through TranslateProjection in index-based binding mode breaks tests like GearsOfWarQuerySqlServerTest.Optional_navigation_type_compensation_works_with_list_initializers.

Metadata

Metadata

Assignees

Type

No fields configured for Bug.

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions