I am trying implement the dreaded GroupBy+First pattern in EF core 3.1.1 (with Pomelo adapter 3.1.1 on MariaDB), which already has an excellet answer using correlated subqueries. However when using this workaround, the EF refuses to translate DateTime.Date
comparison in any form:
var query = from groupKey in _context.Message.Select(x => new { x.MessageType, x.CreatedAt.Date }).Distinct()
from entity in _context.Message
.Select(x => new { x.CreatedAt.Date, Value = x })
.Where(x => x.Value.MessageType == groupKey.MessageType && x.Date == groupKey.Date)
// after removing Date comparison, the query executes ^^^^^^^^^^^^^^^^^^^^^^^^^^
.OrderBy(x => x.Value.CreatedAt)
.Take(1)
select entity.Value;
the goal of query above is to select first message of each type in every day and then materialize the result. Client-side evaulation is unfeasible, because there are tens of thousands messages each day.
If I remove the x.Date == groupKey.Date
part, the query translates and is executed successfully. I have also tried comparing year/month/day separetly but the result was the same - EF throws exception.
How can I change the query so that it works as described?
the exception is pretty much the same as in the workaround's question, the query could not be translated:
System.InvalidOperationException: The LINQ expression '(CROSS APPLY (Projection Mapping: ( SELECT TOP((1)) ((CONVERT((m0.CreatedAt), (date))) AS c), ((m0.Id)), ((m0.Text)), ((m0.CreatedAt)) FROM (Message AS m0) WHERE ((((((CONVERT((m0.CreatedAt), (date))) == (t.c))) || (((Equal((CONVERT((m0.CreatedAt), (date))))) && (Equal((t.c))))))) && (((t.MessageType) == (m0.MessageType)))) ORDER BY ((m0.CreatedAt) ASC) ) AS t0))' could not be translated.
Which is a weird LINQ expression considering the expected translated SQL is quite trivial, the following query is tested and does exactly the same as the LINQ should:
SELECT *
from (select *,
ROW_NUMBER() over (partition by MessageType, YEAR(CreatedAt), MONTH(CreatedAt), DAY(CreatedAt) order by CreatedAt desc )
as rn from Message
) as byDay where byDay.rn = 1;