Skip to content

NavigationExpandingExpressionVisitor moves Select() behind DefaultIfEmpty() #36208

@Ta1sty

Description

@Ta1sty

Question

Hello,

I am currently writing an EntityFramework Provider. While translating the expression produced by NorthwindSelectQueryTestBase.SelectMany_correlated_with_outer_3, I noticed that the NavigationExpandingExpressionVisitor moves the .Select(o => c.City) behind the .DefaultIfEmpty().

I'm probably missing something, but this seems wrong, as
Enumerable.Empty().DefaultIfEmpty().Select(x => "foo")
is not the same as
Enumerable.Empty().Select(x => "foo").DefaultIfEmpty()

So now I'm left wondering how to handle this, because there are valid uses for both cases. Once the NavigationExpandingExpressionVisitor transforms the expression however I can no longer distinguish between these cases.

I would be very thankful if you could give me some pointers on what the actual behaviour should be.

Best Regards

Your code

NorthwindSelectQueryTestBase.SelectMany_correlated_with_outer_3:

from c in ss.Set<Customer>()
from o in ss.Set<Order>().Where(o => c.CustomerID == o.CustomerID).Select(o => c.City).DefaultIfEmpty()
select new { c, o }

Here's what the query looks like just before and after the call to the NavigationExpandingExpressionVisitor in QueryTranslationPreprocessor.cs Process(Expression query)

Before NavigationExpandingExpressionVisitor:

[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression]
  .SelectMany(c => 
    [Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression]
      .Where(o => (c.CustomerID == o.CustomerID))
      .Select(o => c.City)
      .DefaultIfEmpty(), 
    (c, o) => new <>f__AnonymousType0`2(c = c, o = o)
  )

After NavigationExpandingExpressionVisitor:

[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression]
  .SelectMany(c => 
    [Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression]
      .Where(o => (c.CustomerID == o.CustomerID))
      .DefaultIfEmpty()
      .Select(o => c.City),
    (c, c) => new TransparentIdentifier`2(Outer = c, Inner = c))
  .Select(ti => new <>f__AnonymousType0`2(c = ti.Outer, o = ti.Inner))

Stack traces


Verbose output


EF Core version

9.0.0

Database provider

No response

Target framework

.NET 9.0

Operating system

No response

IDE

No response

Metadata

Metadata

Assignees

Type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions