NewExpression constructor translations (e.g. new DateTimeOffset) don't work in projections (Select)
- Dominant language
- C#
- Stars
- 14.8k
- Forks
- 3.4k
- PR merge metrics
- PR metrics pending
Description
## 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
```csharp
// 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:
```csharp
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 { ... }` 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`.
Contributor guide
Assessment
This issue has not been assessed yet.