Consider this code:
_dbContext.Messages
.GroupBy(m => new
{
MinId = m.SenderId <= m.RecipientId ? m.SenderId : m.RecipientId,
MaxId = m.SenderId > m.RecipientId ? m.SenderId : m.RecipientId
})
.Select(gm => gm.OrderByDescending(m => m.SentAt).FirstOrDefault());
By this I group all dialogues of users by their Id's no matter who sent the message. Then I order messages by SentAt date inside the groups and select one last message out of each dialogue. The thing is that this code worked and more over it translated all of it into pure T-Sql (I user SQL Server Profiler to check that). But then I decided to move my Projects from Core 2.1 to 3.1 and now I get this:
The LINQ expression '(GroupByShaperExpression: KeySelector:
new {
MinId = (CASE
WHEN ((m.SenderId) <= (m.RecipientId)) THEN (m.SenderId)
ELSE (m.RecipientId)
END),
MaxId = (CASE
WHEN ((m.SenderId) > (m.RecipientId)) THEN (m.SenderId)
ELSE (m.RecipientId)
END)
},
ElementSelector:(EntityShaperExpression:
EntityType: Message
ValueBufferExpression:
(ProjectionBindingExpression: EmptyProjectionMember)
IsNullable: False
)
).OrderByDescending(m => m.SentAt)
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.
Any idea to fix this up would be appreciated.
P.S. I know that I can dive into T-SQL and write the stored procedure for it but I am still looking a way to implement it with Linq to Entity.
Unfortunately currently EF Core 3.0 / 3.1 only supports server translation of GroupBy
with projection of key / aggregates (similar to SQL).
This is unacceptable since although EF6 also has no client evaluation, it was able to successfully translate such queries.
Until the GroupBy
translation issues get resolved, the workaround is to replace the GroupBy
with 2 correlated subqueries - first containing just the grouping key and second containing the group elements.
In your case it would be something like this:
var source = _dbContext.Messages
.Select(m => new
{
Key = new
{
MinId = m.SenderId <= m.RecipientId ? m.SenderId : m.RecipientId,
MaxId = m.SenderId > m.RecipientId ? m.SenderId : m.RecipientId
},
Message = m
});
var query = source.Select(e => e.Key).Distinct()
.SelectMany(key => source
.Where(e => e.Key.MinId == key.MinId && e.Key.MaxId == key.MaxId)
.Select(e => e.Message)
.OrderByDescending(m => m.SentAt)
.Take(1));
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With