How to group by week in Entity Framework Core?
Asked Answered
E

4

9

In Entity Framework 6 I can use SqlFunctions.DatePart() method:

var byWeek = data.GroupBy(x => SqlFunctions.DatePart("week", x.Date));

But these classes (DbFunctions and SqlFunctions are not available in Entity Framework Core) (reference).

So my question is How can I group by week in Entity Framework core?

Elderberry answered 8/3, 2017 at 15:20 Comment(4)
Since currently all EF Core queries having GroupBy clause are processed in memory, you could safely use x.Date.DayOfYear / 7 or something like that.Elka
@IvanStoev not anymore. group-by querys are compiled to sql since ef core 2.1. see my answer for an working example.Characteristically
Note: To anyone finding this make sure you understand the difference between week and iso_week and which you want. The year of an iso week is not always the same as the calendar year (eg. in 2020 Week 1 began on 2010-12-30). en.wikipedia.org/wiki/ISO_week_dateRasberry
Database functions can now be mapped to CLR functions relatively easy, for example, DatePart.Usa
E
3

My current workaround for the missing functionality is

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

The function GetFirstMondayOfYear:

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

    return dt;
}

This grouping gives the week number for the current year and negative values for previous years.

Later you can get the week name by using this 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}";
    }
}
Elderberry answered 8/3, 2017 at 19:0 Comment(1)
Note that this entire query is going to be done in memory so will likely be very slow.Silverweed
C
2

It is possible to make use of the datepart SQL function by wrapping it with the DbFunctionAttribute. Tricky part is to tell ef core not to handle the datepart type parameter as a string. Example:

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]
                }));
}

Query:

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

some more info: https://github.com/aspnet/EntityFrameworkCore/issues/10404

Characteristically answered 7/6, 2019 at 8:53 Comment(0)
R
0

I happened to be already using a view, so I just added extra columns to that view representing the week, month and year - for easy grouping via EF Core.

OrderDateDt,
DATEPART(week, OrderDateDt) OrderDateDt_Week,
DATEPART(month, OrderDateDt) OrderDateDt_Month,
DATEPART(year, OrderDateDt) OrderDateDt_Year,
Rasberry answered 29/12, 2019 at 22:18 Comment(2)
Note that datepart(week) cannot be used for an indexed view but I think datepart(iso_week) can.Rasberry
also since I answered this I learnt more about ISO_WEEK than I cared to - so be sure to look into which suits your needs. Also ISO_WEEK for today (12/30/19) is actually 1 as it is part of the first week of next year - so you can't use year or you'll end up with 12/30/19 being week one of 2019 which is not right!Rasberry
C
0

We created a Table which contains one row per date plus the corresponding iso_week. Now you can join on the date and group by the iso_week column

Curnin answered 23/4 at 7:22 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.