Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Problem with EF OrderBy after migration to .net core 3.1

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.

like image 733
Alex Avatar asked Dec 15 '19 17:12

Alex


1 Answers

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));
like image 194
Ivan Stoev Avatar answered Oct 30 '22 21:10

Ivan Stoev