【问题标题】:GroupBy first result of lookup table in Entity Framework CoreGroupBy 在 Entity Framework Core 中查找表的第一个结果
【发布时间】:2017-01-10 13:00:49
【问题描述】:

我正在尝试获取某个日期范围内每个部分的志愿者工作总天数。有一个出勤表,其中包括要映射到成员表的日期和成员 ID。还有一个 section 表来列出所有 section 和一个 membertosection 表,因为一个成员可以在多个 section 中。

我想按成员所在的第一个部分进行分组,然后计算该部分的所有出勤天数。

以下内容在我按成员表上的一对多字段(类别)分组的情况下非常有效,但我不知道如何将其更改为第一部分(如果成员有一个部分)

var result = await _context.Attendance
                .Where(p => p.Day >= firstDayOfLastQuarter && p.Day <= lastDayOfLastQuarter)
                .GroupBy(p => p.Member.Category.Name)
                .Select(p => new { key = p.Key, count = p.Count() })
                .ToListAsync();

我正在寻找类似的东西

var result = await _context.Attendance
                .Where(p => p.Day >= firstDayOfLastQuarter && p.Day <= lastDayOfLastQuarter && p.Member.MemberToSection.Any(s => s.MemberId == p.MemberId))
                .GroupBy(p => p.Member.MemberToSection.First().Section.Name)
                .Select(p => new { key = p.Key, count = p.Count() })
                .ToListAsync();

更新

产生所需结果的 T-Sql 代码是

SELECT Count(*) as Total, Section.Name
FROM Attendance
LEFT JOIN Member on Attendance.Member_Id = Member.Id
OUTER APPLY (
  SELECT TOP(1) sec.id, sec.Name
  FROM MemberToSection m2s
  LEFT JOIN Section sec ON m2s.Section_Id = sec.Id
  WHERE m2s.Member_Id = Member.Id 
) as Section
WHERE Attendance.Day >= '2016-10-01' AND Attendance.Day <= '2016-12-31'
GROUP BY Section.Name

“类似”的代码返回错误

An unhandled exception occurred while processing the request.

ArgumentException: Property 'System.String Name' is not defined for type 'Microsoft.EntityFrameworkCore.Storage.ValueBuffer'

这里要求的是相关类

出勤率.cs

public partial class Attendance
{
    public long Pk { get; set; }
    public int MemberId { get; set; }
    public DateTime Day { get; set; }

    public virtual Member Member { get; set; }
}

会员.cs

public partial class Member
{
    public Member()
    {
        MemberToSection = new HashSet<MemberToSection>();
    }

    public int Id { get; set; }
    public int? CategoryId { get; set; }

    public virtual ICollection<MemberToSection> MemberToSection { get; set; }
}

MemberToSection.cs

public partial class MemberToSection
{
    public int SectionId { get; set; }
    public int MemberId { get; set; }

    public virtual Member Member { get; set; }
    public virtual Section Section { get; set; }
}

Section.cs

public partial class Section
{
    public Section()
    {
        MemberToSection = new HashSet<MemberToSection>();
    }

    public int Id { get; set; }
    public string Name { get; set; }

    public virtual ICollection<MemberToSection> MemberToSection { get; set; }
}

【问题讨论】:

  • 能把相关类的映射贴出来吗?
  • 您的 something like 查询有什么问题?
  • 我不认为你可以这样做:.GroupBy(p => p.Member.MemberToSection.First().Section.Name)。您应该先选择,然后在一个字段上分组。像这样: Where...Select( p=> new { p.Member.MemberToSection.First().Section.Name}).GroupBy ....

标签: c# entity-framework asp.net-core entity-framework-core


【解决方案1】:

(当前组合的)EF documentationCompare EF Core & EF6.x 部分开头,其中包含非常“有用”的主题 Which One Is Right for You。好吧,看起来 EF Core 不适合你(还)。以下适用于目前最新的 EF Core v1.1.0。

首先,GroupBy(即使是简单的原始属性)总是在内存中处理。

其次,在处理像您这样非常有效的 LINQ 查询时,有很多内部错误会导致异常。

第三,经过反复试验,以下等效结构适用于您的情况(至少不会产生异常):

.GroupBy(p => p.Member.MemberToSection.Select(m => m.Section.Name).FirstOrDefault())

(顺便说一句,与问题无关,p.Member.MemberToSection.Any 调用中的s =&gt; s.MemberId == p.MemberId 条件是多余的,因为它是由关系强制执行的,所以简单的Any() 也会这样做。)

但是现在不仅GroupBy是在内存中执行的,而且查询也导致了N+1个类似于EF Core nested Linq select results in N + 1 SQL queries的SQL查询。恭喜:(

【讨论】:

  • 嗨,“GroupBy 总是在内存中处理”是否等于 GroupBy 在客户端处理? IE。这种方法会导致性能问题(如果需要的表包含大量数据)?
  • @Mergasov 确实如此。 EF Core Roadmap 中有一个项目 GroupBy 翻译会将 LINQ GroupBy 运算符的翻译移动到数据库中,而不是在内存中,但我看不到它应该何时实现。跨度>
猜你喜欢
  • 2019-02-14
  • 1970-01-01
  • 2023-04-05
  • 1970-01-01
  • 2018-11-02
  • 2019-04-17
  • 2019-07-05
  • 1970-01-01
  • 2022-01-09
相关资源
最近更新 更多