EF 3.1: Overcome LINQ GroupBy SQL translation problem [duplicate]

[亡魂溺海] 提交于 2020-02-21 06:11:38

问题


In MS SQL Server I have a table that contains a history of calls to contacts (that is another table). Accessed by EF, The Entities are the following:

public partial class CallbackHistory
{
    public int HistoryId { get; set; }
    public int CompanyId { get; set; }
    public int CallerId { get; set; }
    public DateTime LastCallTimeStamp { get; set; }

    public virtual CompanyDiary Caller { get; set; }
    public virtual Company Company { get; set; }
}

and

public partial class CompanyDiary
{
    public CompanyDiary()
    {
        DatiCallbackHistory = new HashSet<DatiCallbackHistory>();
    }
    public int CallerId { get; set; }
    public string NickName { get; set; }
    public string PhoneNumber { get; set; }
    public string Email { get; set; }
    public int CompanyId { get; set; }

    public virtual Company Company { get; set; }
    public virtual ICollection<CallbackHistory> CallbackHistory { get; set; }
}

I need to get a list of the last 5 calls to individual numbers order by date descending.

I came up with the following query that could not be translated to SQL, unfortunately:

var historyOfCalls = await
                    context.CallbackHistoryDbSet
                    .Include(historyEntry => historyEntry.Caller)
                    .Where(historyEntry => historyEntry.CompanyId == companyId)
                    .GroupBy(s => s.Caller.PhoneNumber)
                    .Select(s => s.OrderByDescending(historyEntry => historyEntry.LastCallTimeStamp).FirstOrDefault())
                    .Take(5)
                    .AsNoTracking()
                    .ToListAsync(cancellationToken).ConfigureAwait(false);

Here is the error I get:

System.AggregateException
  HResult=0x80131500
  Message=One or more errors occurred. (The LINQ expression '(GroupByShaperExpression:
KeySelector: (c.PhoneNumber), 
ElementSelector:(EntityShaperExpression: 
    EntityType: CallbackHistory
    ValueBufferExpression: 
        (ProjectionBindingExpression: EmptyProjectionMember)
    IsNullable: False
)
)
    .OrderByDescending(historyEntry => historyEntry.LastCallTimeStamp)' 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.)
  Source=System.Private.CoreLib

Inner Exception 1:
InvalidOperationException: The LINQ expression '(GroupByShaperExpression:
KeySelector: (c.PhoneNumber), 
ElementSelector:(EntityShaperExpression: 
    EntityType: CallbackHistory
    ValueBufferExpression: 
        (ProjectionBindingExpression: EmptyProjectionMember)
    IsNullable: False
)
)
    .OrderByDescending(historyEntry => historyEntry.LastCallTimeStamp)' 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.

It seems that the problem lies in the fact that I'm grouping on a navigation property.

Can I rewrite this query to make it translatable to SQL?

I have no clue when to switch to Linq to objects with this query As I already have a call to ToListAsync. I've tried to move it after Select in the query but it does not compile


回答1:


Calling ToListAsync earlier in your query will result all other linq statements to not compiling because to ToListAsync will return a Task so essentially your will need to await to result first or call .Result (which will be a blocking for the current thread). My suggestion here would be to split up the query in:

  1. Getting the data
  2. Projecting the data

e.g.

    var historyOfCalls = await context.CallbackHistoryDbSet
        .Include(historyEntry => historyEntry.Caller)
        .Where(historyEntry => historyEntry.CompanyId == companyId)
        .AsNoTracking()
        .ToListAsync(cancellationToken).ConfigureAwait(false);

    var projection = historyOfCalls 
        .GroupBy(s => s.Caller.PhoneNumber);

Remember that by calling group by you get a Grouping< T, TV >, so when call Select you have a Key property (the phone number) and and value property. I would suggest reversing you get query by using the caller DbSet and include its caller history, then grouping from there and use some of the overloads on group by to select to correct values into TV.

    var callers = await context.CompanyDiaryDbSet
        .Include(c => c.CallbackHistory)
        .Where(c=> c.CompanyId == companyId)
        .AsNoTracking()
        .ToListAsync(cancellationToken).ConfigureAwait(false);


来源:https://stackoverflow.com/questions/59892940/ef-3-1-overcome-linq-groupby-sql-translation-problem

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!