dotnet / dotnet/efcore

Wrong order by clause generated when sorting by tph property

Open
#29,856 5 comments 0 reactions 0 assignees View on GitHub
area-query customer-reported ef6-parity
Dominant language
C#
Stars
14.8k
Forks
3.4k
PR merge metrics
PR metrics pending

Description

It seems that ordering by a property which is projected like below with type checks cannot be translated.

Example for EFCore:

```C#
public class UserContext : DbContext
{
public DbSet Parents { get; set; }

protected override void OnConfiguring(DbContextOptionsBuilder options)
=> options.UseSqlServer(@"data source=(local);Integrated Security=SSPI;initial catalog=User_Core;MultipleActiveResultSets=True;Encrypt=False");
}

public class Parent
{
public int Id { get; set; }
public string Name { get; set; }
public Child Child { get; set; }
}

public class Child
{
public int Id { get; set; }
public string Name { get; set; }
}

public class ChildDerived1 : Child
{
public string Derived1Info { get; set; }
}

public class ChildDerived2 : Child
{
public string Derived2Info { get; set; }
}

class Program
{
static void Main(string[] args)
{
using var db = new UserContext();
db.Database.EnsureDeleted();
db.Database.EnsureCreated();

var parent1 = new Parent
{
Name = "a",
Child = new ChildDerived1 { Derived1Info = "Info1", Name = "Derived1" }
};
db.Parents.Add(parent1);

var parent2 = new Parent
{
Name = "b",
Child = new ChildDerived2 { Derived2Info = "Info2", Name = "Derived2" }
};
db.Parents.Add(parent2);

db.SaveChanges();

var parents = db.Parents.Select(p => new
{
p.Name,
DerivedInfo = p.Child is ChildDerived1
? (p.Child as ChildDerived1).Derived1Info
: p.Child is ChildDerived2
? (p.Child as ChildDerived2).Derived2Info
: "Access Denied"
})
.OrderByDescending(b => b.DerivedInfo)
.ToList();
}
}
```

Stacktrace from EFCore:

```
System.InvalidOperationException: The LINQ expression 'DbSet()
.LeftJoin(
inner: DbSet(),
outerKeySelector: p => EF.Property(p, "ChildId"),
innerKeySelector: c => EF.Property(c, "Id"),
resultSelector: (o, i) => new TransparentIdentifier(
Outer = o,
Inner = i
))
.OrderByDescending(p => (p.Inner is ChildDerived1) ? (p.Inner as ChildDerived1).Derived1Info : (p.Inner is ChildDerived2) ? (p.Inner as ChildDerived2).Derived2Info : "Access Denied")' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to 'AsEnumerable', 'AsAsyncEnumerable', 'ToList', or 'ToListAsync'. See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.
at Microsoft.EntityFrameworkCore.Query.QueryableMethodTranslatingExpressionVisitor.g__CheckTranslated|15_0(ShapedQueryExpression translated, <>c__DisplayClass15_0& )
at Microsoft.EntityFrameworkCore.Query.QueryableMethodTranslatingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
at Microsoft.EntityFrameworkCore.Query.RelationalQueryableMethodTranslatingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
at System.Linq.Expressions.MethodCallExpression.Accept(ExpressionVisitor visitor)
at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
at Microsoft.EntityFrameworkCore.Query.QueryableMethodTranslatingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
at Microsoft.EntityFrameworkCore.Query.RelationalQueryableMethodTranslatingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
at System.Linq.Expressions.MethodCallExpression.Accept(ExpressionVisitor visitor)
at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
at Microsoft.EntityFrameworkCore.Query.QueryableMethodTranslatingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
at Microsoft.EntityFrameworkCore.Query.RelationalQueryableMethodTranslatingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
at System.Linq.Expressions.MethodCallExpression.Accept(ExpressionVisitor visitor)
at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
at Microsoft.EntityFrameworkCore.Query.QueryCompilationContext.CreateQueryExecutor[TResult](Expression query)
at Microsoft.EntityFrameworkCore.Storage.Database.CompileQuery[TResult](Expression query, Boolean async)
at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.CompileQueryCore[TResult](IDatabase database, Expression query, IModel model, Boolean async)
at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.<>c__DisplayClass9_0`1.b__0()
at Microsoft.EntityFrameworkCore.Query.Internal.CompiledQueryCache.GetOrAddQuery[TResult](Object cacheKey, Func`1 compiler)
at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.Execute[TResult](Expression query)
at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryProvider.Execute[TResult](Expression expression)
at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable`1.GetEnumerator()
at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
```

Query generated from EF6:

```
SELECT
[Project1].[C1] AS [C1],
[Project1].[Name] AS [Name],
[Project1].[C2] AS [C2]
FROM ( SELECT
[Extent1].[Name] AS [Name],
1 AS [C1],
CASE WHEN ([Extent2].[Discriminator] = N'ChildDerived1') THEN CASE WHEN ([Extent2].[Discriminator] = N'ChildDerived1') THEN [Extent2].[Derived1Info] END WHEN ([Extent2].[Discriminator] = N'ChildD
erived2') THEN CASE WHEN ([Extent2].[Discriminator] = N'ChildDerived2') THEN [Extent2].[Derived2Info] END ELSE N'Access Denied' END AS [C2]
FROM [dbo].[Parents] AS [Extent1]
LEFT OUTER JOIN [dbo].[Children] AS [Extent2] ON ([Extent2].[Discriminator] IN (N'ChildDerived1',N'ChildDerived2',N'Child')) AND ([Extent1].[Child_Id] = [Extent2].[Id])
) AS [Project1]
ORDER BY [Project1].[C2] DESC
```

EF Core version: 7.0.0
Database provider: Microsoft.EntityFrameworkCore.SqlServer
Target framework: .NET 6.0
Operating system: Windows 10
IDE: Visual Studio 2022 17.4.1

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.