LINQ - 以月为单位拆分结果

LINQ - Split results in months

我正在使用 Entity Framework,并且我有以下 classes:

public sealed class Class
{
    [Key]
    public int Id { get; init; }

    public IEnumerable<Student> Students { get; init; } = null!;
}

public sealed class Student
{
    [Key]
    public int Id { get; init; }

    public IEnumerable<Point> Points { get; init; } = null!;

public sealed class Point
{
    [Key]
    public int Id { get; init; }

    public DateTime DateTime { get; set; }

    public int MathPoints { get; set; }

    public int LanguagePoints { get; set; }

现在,我需要编写一个查询,它选择一个 class,但是对于那个 class,创建一个包含 12 个元素的集合(每个月一个),其中每个集合包含一组有限的人(例如 2),以及他们的分数。

输出类似于:

[
  {
    "date": "2022/01/01",
    "totalPoints": 195, // This is the sum of ALL points of ALL students in this month.
    "students": [
      {
        "name": "student 1",
        "points": 100 // This is a sum of the `MathPoints` and the `LanguagePoints`.
      },
      {
        "name": "student 2",
        "points": 75 // This is a sum of the `MathPoints` and the `LanguagePoints`.
      },
      {
        "name": "others",
        "points": 20 // This is a sum of the `MathPoints` and the `LanguagePoints`.
      }
    ]
  },
  // .. Repeated here for all the other months in the year.
]

我知道这可以通过执行一些 linq 查询来实现,但我希望它尽可能优化。

谁能提供一些有关如何解决此问题的信息?

编辑:

应返回一个IEnumerable,其中包含以下字段:

public sealed class Report
{
    public DateTime Date { get; set; }

    public int TotalPoints { get; set; }

    public IEnumerable<StudentReport> StudentReports { get; set; }
}
public sealed class StudentReport
{
    public string Name { get; set; }

    public int Points { get; set; }
}

您必须将此类查询分成两部分 - 加载最少的所需数据并在客户端分组。需要在客户端分组,因为您的结果包含分组的详细信息。

// input parameters
var classId = ...;
var year = 2022;

// parameters for range filter 
var startDate = new DateTime(year, 1, 1);
var endDate = startDate.AddYear(1);

var query = 
    from c in context.Class
    from s in c.Students
    from p in s.Points
    where c.Id == classId &&
        p.DateTime >= startDate && p.DateTime < endDate
    group p by new { p.DateTime.Year, p.DateTime.Month, s.Id, s.Name } into g
    select new 
    {
        Year = g.Key.Year,
        Month = g.Key.Month,
        Points = g.Sum(x => x.MathPoints + x.LanguagePoints),
        Name = g.Key.Name
    };

// materialize items, grouping should be provided on the client side
var rawItems = await query.ToListAsync();

var resultQuery = 
    from r in rawItems
    group r by new { r.Year, r.Month } into g
    select new Report
    {
        Date = new DateTime(g.Key.Year, g.Key.Month, 1),
        TotalPoints = g.Sum(x => x.Points),
        StudentReports = g.Select(x => new StudentReport
        {
            Name = x.Name,
            Points = x.Points
        })
        .ToList()
    }

var result = resultQuery.ToList();