I am trying to do some projections in LINQ to convert to DTOs. The goal is to do it with expressions so that it is translated directly to SQL.
It works perfectly if I do everything inlined like this :
return workHeader.Select(x => new WorkHeaderDto
{
Id = x.Id,
StartDate = x.Works.Min(w => w.StartDate),
EndDate = x.Works.Min(w => w.EndDate),
Parcelles = x.Works.SelectMany(w => w.Parcelles).Distinct(),
Tools = x.Works.SelectMany(w => w.Tools).Select(t => t.Tool).Distinct().OrderBy(t => t.ToolId).AsQueryable().Select(t => new ToolDto
{
RankNumber = t.RankNumber,
Comments = t.Comments,
OrderNumber = t.OrderNumber,
Garage = t.Garage,
Maintained = t.Maintained,
OI = t.OI,
})
}
);
This way the projection is actually done in SQL, selecting only the needed columns from Tool.
But I want the mapping code to be reusable, so I first tried to put it inside a static method in another file and use it like this :
return workHeader.Select(x => new WorkHeaderDto
{
Id = x.Id,
StartDate = x.Works.Min(w => w.StartDate),
EndDate = x.Works.Min(w => w.EndDate),
Parcelles = x.Works.SelectMany(w => w.Parcelles).Distinct(),
Tools = x.Works.SelectMany(w => w.Tools).Select(t => t.Tool).Distinct().OrderBy(t => t.ToolId).AsQueryable().Select(t => DtoMapper.ToDto(t))
}
);
But when I do this it is not considered an expression for some reasons, and the SQL query will load all fields from tools and do the mapping in memory.
So I’ve tried the following :
static Expression<Func<Tool, ToolDto>> ConvertExpression = t1 => new ToolDto
{
RankNumber = t.RankNumber,
Comments = t.Comments,
OrderNumber = t.OrderNumber,
Garage = t.Garage,
Maintained = t.Maintained,
OI = t.OI,
};
return workHeader.Select(x => new WorkHeaderDto
{
Id = x.Id,
StartDate = x.Works.Min(w => w.StartDate),
EndDate = x.Works.Min(w => w.EndDate),
Parcelles = x.Works.SelectMany(w => w.Parcelles).Distinct(),
Tools = x.Works.SelectMany(w => w.Tools).Select(t => t.Tool).Distinct().OrderBy(t => t.ToolId).AsQueryable().Select(ConvertExpression)
}
);
But now the request doesn’t work at all. Do note that it is used by HotChocolate for a GraphQL query.
It works perfectly when inlined (projection in SQL and the good result), not perfectly when calling a method (projection in memory with full select in SQL, but good result) and not at all with the expression (graphql returns an error).
What is the best way to reuse the mapping code but keep doing the projection in sql ?
4
There's a lot of information missing.
workHeader
must be an already created EF Core LINQ query. All supported EF Core version would throw an exception instead of loading entire objects. The last version that didn't was EF Core 2 and even that generated runtime warnings when it used client-side evaluation. The static method you mention should work if it receives an IQueryable namedworkHeader
and returns new new one. After all,Select
itself is a method that receives an IQueryable and returns a new one2 hours ago
Word salad…..
1 hour ago
Have you looked into ProjectTo in Automapper?
1 hour ago
Does this answer your question? Can I reuse code for selecting a custom DTO object for a child property with EF Core?
1 hour ago