Condition and Ordering when projecting inheritance tree of entities don't work
See original GitHub issueHi, i know the subject as been discussed a lot and it’s complex but i wanted to share my interest to this issue. I’m trying to project a set of entities with some inheritance. I’ve made a sample project to show my issue.
Steps to reproduce
Entities : I’ve got 3 Db Entities (each mapped to their class, the issue is the same with any inheritance db model) MainEntity (the base abstract class) SubEntity1 : MainEntity SubEntity2 : MainEntity
Dtos: I’ve got 3 dtos model MainDto (also abstract) SubDto1 : MainDto SubDto2 : MainDto
A “by hand” automapper conversion method that transform Entities to Model (and vise versa), because i would like to use automapper to do the conversion but wanted to be sure it’s an ef core issue.
Now the issue : if i try to create a query of all MainEntities (SubEntity1 + SubEntity2) the request crash when i add a WHERE condition or an ORDERBY condition.
IQueryable<MainDto> query = context.Entities.ToModel();
//Those call are working !
IEnumerable<MainDto> entites = query.ToList();
IEnumerable<MainDto> entitesSkipTake = query.Skip(5).Take(5).ToList();
//Those ones don't
IEnumerable<MainDto> entitesOrderBy = query.OrderBy(p => p.Id).ToList();
IEnumerable<MainDto> entitesWhere = query.Where(p => p.Property0.Contains("Test")).ToList();
The thing that seems strange is that the “simple” select and projection are working and give this sql requests :
SELECT [e].[Id], [e].[Property0], [s].[Property1], [s0].[Property2], CASE
WHEN [s].[Id] IS NOT NULL THEN CAST(1 AS bit)
ELSE CAST(0 AS bit)
END AS [IsSubEntity1], CASE
WHEN [s0].[Id] IS NOT NULL THEN CAST(1 AS bit)
ELSE CAST(0 AS bit)
END AS [IsSubEntity2]
FROM [ENTITIY] AS [e]
LEFT JOIN [SUBENTITY1] AS [s] ON [e].[Id] = [s].[Id]
LEFT JOIN [SUBENTITY2] AS [s0] ON [e].[Id] = [s0].[Id]
then the skip and take one :
SELECT [e].[Id], [e].[Property0], [s].[Property1], [s0].[Property2], CASE
WHEN [s].[Id] IS NOT NULL THEN CAST(1 AS bit)
ELSE CAST(0 AS bit)
END AS [IsSubEntity1], CASE
WHEN [s0].[Id] IS NOT NULL THEN CAST(1 AS bit)
ELSE CAST(0 AS bit)
END AS [IsSubEntity2]
FROM [ENTITIY] AS [e]
LEFT JOIN [SUBENTITY1] AS [s] ON [e].[Id] = [s].[Id]
LEFT JOIN [SUBENTITY2] AS [s0] ON [e].[Id] = [s0].[Id]
ORDER BY (SELECT 1)
OFFSET @__p_0 ROWS FETCH NEXT @__p_0 ROWS ONLY
but when it comes for the order by and the where it’s crashing with the known :
.OrderBy(m => m
.ToModel().Id)' 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 either AsEnumerable(), AsAsyncEnumerable(), ToList(), or ToListAsync(). See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.
the thing is it would seems that the query could easily be made in sql (at a first see of course) the order by one could be :
SELECT [e].[Id], [e].[Property0], [s].[Property1], [s0].[Property2], CASE
WHEN [s].[Id] IS NOT NULL THEN CAST(1 AS bit)
ELSE CAST(0 AS bit)
END AS [IsSubEntity1], CASE
WHEN [s0].[Id] IS NOT NULL THEN CAST(1 AS bit)
ELSE CAST(0 AS bit)
END AS [IsSubEntity2]
FROM [ENTITIY] AS [e]
LEFT JOIN [SUBENTITY1] AS [s] ON [e].[Id] = [s].[Id]
LEFT JOIN [SUBENTITY2] AS [s0] ON [e].[Id] = [s0].[Id]
ORDER BY [e].[Id]
and the WHERE one
SELECT [e].[Id], [e].[Property0], [s].[Property1], [s0].[Property2], CASE
WHEN [s].[Id] IS NOT NULL THEN CAST(1 AS bit)
ELSE CAST(0 AS bit)
END AS [IsSubEntity1], CASE
WHEN [s0].[Id] IS NOT NULL THEN CAST(1 AS bit)
ELSE CAST(0 AS bit)
END AS [IsSubEntity2]
FROM [ENTITIY] AS [e]
LEFT JOIN [SUBENTITY1] AS [s] ON [e].[Id] = [s].[Id]
LEFT JOIN [SUBENTITY2] AS [s0] ON [e].[Id] = [s0].[Id]
WHERE [e].[Id] IS NOT NULL AND ([e].[Property0] LIKE N'%Test%')
So now the question what am i not understanding that make ef core not able to handle this kind of request… (yes because it could be so cool that it could !!!)
Thanks for all your work and your amazing tools !
Further technical details
EF Core version: 3.1.0 and 5.0.0-preview.8.20407.4 Database provider: Microsoft.EntityFrameworkCore.SqlServer@5.0.0-preview.8.20407.4 Target framework: .NET Core 3.1 Operating system: IDE: Visual Studio 2019 16.4
Issue Analytics
- State:
- Created 3 years ago
- Comments:7 (4 by maintainers)
They are not same query as the one in the repro code. Specifically
ToModel
method call is not inside any lambda passed to another method. When you useToModel
outside of lambda then it will actually be inlined in the tree. When you use that inside a lambda function, compiler injects ToModel method in the tree. We cannot look into ToModel method if it is integrated in the tree.Note from triage: putting on the backlog to consider supporting this kind translation in the future.