如何在Entity Framework Core中按周分组?

9
在Entity Framework 6中,我可以使用SqlFunctions.DatePart()方法:
var byWeek = data.GroupBy(x => SqlFunctions.DatePart("week", x.Date));

但是这些类(DbFunctionsSqlFunctions)在Entity Framework Core中不可用(参考)。

因此,我的问题是如何在Entity Framework Core中按周分组?


3
目前,所有包含GroupBy子句的EF Core查询都是在内存中处理的,因此您可以安全地使用x.Date.DayOfYear / 7或类似的方式。 - Ivan Stoev
1
@IvanStoev 不再需要了。自从 EF Core 2.1 版本以后,分组查询已被编译成 SQL。请看我的答案中的一个工作示例。 - cyptus
注意:对于任何发现此内容的人,请确保您理解周和iso_week之间的区别以及您想要哪一个。 iso周的年份并不总是与日历年相同(例如,在2020年,第1周始于2010-12-30)。https://en.wikipedia.org/wiki/ISO_week_date - Simon_Weaver
3个回答

3

目前我对缺失功能的解决方法是

var firstMondayOfYear = this.GetFirstMondayOfYear(DateTime.Now.Year);
var entries =
    this.entitiesService.FindForLastMonths(this.CurrentUser.Id, 6)
        .GroupBy(x => ((int)(x.Date - firstMondayOfYear).TotalDays / 7))

函数GetFirstMondayOfYear

private DateTime GetFirstMondayOfYear(int year)
{
    var dt = new DateTime(year, 1, 1);
    while (dt.DayOfWeek != DayOfWeek.Monday)
    {
        dt = dt.AddDays(1);
    }

    return dt;
}

此分组将为当前年份提供周数,并为以前的年份提供负值。

稍后,您可以使用此getter获取周名称:

public string WeekName
{
    get
    {
        var year = DateTime.Now.AddYears((int)Math.Floor(this.WeekNumber / 52.0)).Year;
        var weekNumber = this.WeekNumber % 52;
        while (weekNumber < 0)
        {
            weekNumber += 52;
        }

        return $"{year}, W{weekNumber}";
    }
}

请注意,整个查询将在内存中完成,因此可能非常缓慢。 - DavidG

2
可以通过使用DbFunctionAttribute将datepart SQL函数包装起来来进行使用。 棘手的部分是告诉EF Core不要将datepart类型参数处理为字符串。例如:
DbContext:
public int? DatePart(string datePartArg, DateTime? date) => throw new Exception();

public void OnModelCreating(DbModelBuilder modelBuilder) {
    var methodInfo = typeof(DbContext).GetRuntimeMethod(nameof(DatePart), new[] { typeof(string), typeof(DateTime) });
    modelBuilder
        .HasDbFunction(methodInfo)
        .HasTranslation(args => new SqlFunctionExpression(nameof(DatePart), typeof(int?), new[]
                {
                        new SqlFragmentExpression(args.ToArray()[0].ToString()),
                        args.ToArray()[1]
                }));
}

查询:
repository.GroupBy(x => dbContext.DatePart("week", x.CreatedAt));

更多信息:https://github.com/aspnet/EntityFrameworkCore/issues/10404

0

我恰好已经在使用一个视图,所以我只需向该视图添加额外的列,表示周、月和年 - 以便通过 EF Core 进行轻松分组。

OrderDateDt,
DATEPART(week, OrderDateDt) OrderDateDt_Week,
DATEPART(month, OrderDateDt) OrderDateDt_Month,
DATEPART(year, OrderDateDt) OrderDateDt_Year,

请注意,datepart(week)不能用于索引视图,但我认为可以使用datepart(iso_week)。 - Simon_Weaver
自从我回答这个问题以来,我了解了更多关于ISO_WEEK的知识,所以请务必了解哪种更适合您的需求。另外,今天(2019年12月30日)的ISO_WEEK实际上是1,因为它是下一年的第一周的一部分。所以你不能使用 year,否则你会得到2019年的第一周是12/30/19,这是不正确的! - Simon_Weaver

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接