如何在 EF Core 3.1 中执行 GroupBy

How to perform GroupBy in EF Core 3.1

我们已将应用程序从 2.1 升级到 Core 3.1。以下查询不再有效。 重写它的最佳方法是什么?

    public async Task<Dictionary<int, CoStatusUpdate>> GetDailyStatusUpdates(int organisationTypeId)
    {
        var dailyUpdates = await _context.CoStatusUpdates
            .Include(x => x.CoStatus)
            .Include(x => x.Company).ThenInclude(x => x.Organisation)
            .GroupBy(p => p.CompanyId)
                .Select(x => new CoStatusUpdate
                {
                    CompanyId = x.Key,
                    Company = new Company() //building new object for the only fields we need otherwise there's way more returned than needed
                    {
                        Organisation = new Organisation()
                        {
                            Name = x.FirstOrDefault(y => y.SubmittedDateTime == x.Max(z => z.SubmittedDateTime)).Company.Organisation.Name,
                            IsDeleted = x.FirstOrDefault(y => y.SubmittedDateTime == x.Max(z => z.SubmittedDateTime)).Company.Organisation.IsDeleted,
                            OrganisationTypeId = x.FirstOrDefault(y => y.SubmittedDateTime == x.Max(z => z.SubmittedDateTime)).Company.Organisation.OrganisationTypeId,
                        }
                    },
                    CoStatusUpdateId = x.FirstOrDefault(y => y.SubmittedDateTime == x.Max(z => z.SubmittedDateTime)).CoStatusUpdateId,
                    CoStatusId = x.FirstOrDefault(y => y.SubmittedDateTime == x.Max(z => z.SubmittedDateTime)).CoStatusId,
                    CoStatus = x.FirstOrDefault(y => y.SubmittedDateTime == x.Max(z => z.SubmittedDateTime)).CoStatus,
                    SubmittedDateTime = x.Max(z => z.SubmittedDateTime)
                }).Where(x => !x.Company.Organisation.IsDeleted
                     && x.Company.Organisation.OrganisationTypeId == organisationTypeId))
            .ToDictionaryAsync(x => x.CompanyId);

        return dailyUpdates;
    }

错误是:

{"The LINQ expression '(GroupByShaperExpression:\r\nKeySelector: (f.CompanyId), \r\nElementSelector:(EntityShaperExpression: \r\n EntityType: CoStatusUpdate\r\n ValueBufferExpression: \r\n (ProjectionBindingExpression: EmptyProjectionMember)\r\n IsNullable: False\r\n)\r\n)\r\n .FirstOrDefault(y => y.SubmittedDateTime == (GroupByShaperExpression:\r\n KeySelector: (f.CompanyId), \r\n ElementSelector:(EntityShaperExpression: \r\n EntityType: CoStatusUpdate\r\n ValueBufferExpression: \r\n (ProjectionBindingExpression: EmptyProjectionMember)\r\n IsNullable: False\r\n )\r\n )\r\n .Max(z => z.SubmittedDateTime))' 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."}

编辑:

我的场景的解决方案

虽然我接受了给出的第一个答案(它提供了与此查询在 Core 2.1 中已经做的等效)我现在找到了一种实际执行分组服务器端的方法,它的性能要高得多。

        var coUpdates = await _context.Companys
            .Where(p=>!p.Organisation.IsDeleted)  
            .Select(p => p.CompanyId)
          //.Distinct() //if you wouldn't already be getting a unique list of id's
            .Select(id => _context.coStatusUpdates
                .Include(u=>u.coStatus)
                .Include(u=>u.Company).ThenInclude(x=>x.Organisation)
                .OrderByDescending(p => p.SubmittedDateTime)  
                .FirstOrDefault(p => p.CompanyId == id))  
            .ToListAsync();

        var coUpdatesDictionary = coUpdates
            .Where(x => x != null)
            .ToDictionary(x=>x.CompanyId);

        return coUpdatesDictionary;

您需要将其拆分为两个不同的查询:

var dailyUpdates = await _context.CoStatusUpdates
            .Include(x => x.CoStatus)
            .Include(x => x.Company)
               .ThenInclude(x => x.Organisation)
            .Where(x => !x.Company.Organisation.IsDeleted
                     && x.Company.Organisation.OrganisationTypeId == organisationTypeId))
            .ToListAsync();

var result = dailyUpdates.GroupBy(p => p.CompanyId)
                .Select(x => new CoStatusUpdate
                {
                    CompanyId = x.Key,
                    Company = new Company() //building new object for the only fields we need otherwise there's way more returned than needed
                    {
                        Organisation = new Organisation()
                        {
                            Name = x.FirstOrDefault(y => y.SubmittedDateTime == x.Max(z => z.SubmittedDateTime)).Company.Organisation.Name,
                            IsDeleted = x.FirstOrDefault(y => y.SubmittedDateTime == x.Max(z => z.SubmittedDateTime)).Company.Organisation.IsDeleted,
                            OrganisationTypeId = x.FirstOrDefault(y => y.SubmittedDateTime == x.Max(z => z.SubmittedDateTime)).Company.Organisation.OrganisationTypeId,
                        }
                    },
                    CoStatusUpdateId = x.FirstOrDefault(y => y.SubmittedDateTime == x.Max(z => z.SubmittedDateTime)).CoStatusUpdateId,
                    CoStatusId = x.FirstOrDefault(y => y.SubmittedDateTime == x.Max(z => z.SubmittedDateTime)).CoStatusId,
                    CoStatus = x.FirstOrDefault(y => y.SubmittedDateTime == x.Max(z => z.SubmittedDateTime)).CoStatus,
                    SubmittedDateTime = x.Max(z => z.SubmittedDateTime)
                })
            .ToDictionary(x => x.CompanyId);