在LINQ to Entities中按周分组

Str*_*ior 39 c# linq linq-to-entities entity-framework

我有一个应用程序,允许用户输入他们工作的时间,我正在尝试为此构建一个良好的报告,利用LINQ to Entities.因为每个TrackedTimeTargetDate只是a的"Date"部分DateTime,所以按用户和日期对时间进行分组相对简单(为简单起见,我省略了"where"子句):

var userTimes = from t in context.TrackedTimes
                group t by new {t.User.UserName, t.TargetDate} into ut
                select new
                {
                    UserName = ut.Key.UserName,
                    TargetDate = ut.Key.TargetDate,
                    Minutes = ut.Sum(t => t.Minutes)
                };
Run Code Online (Sandbox Code Playgroud)

由于该DateTime.Month属性,按用户分组和Month只是稍微复杂一点:

var userTimes = from t in context.TrackedTimes
                group t by new {t.User.UserName, t.TargetDate.Month} into ut
                select new
                {
                    UserName = ut.Key.UserName,
                    MonthNumber = ut.Key.Month,
                    Minutes = ut.Sum(t => t.Minutes)
                };
Run Code Online (Sandbox Code Playgroud)

现在是棘手的部分.是否有可靠的方式按周分组?我根据对类似LINQ to SQL问题的响应尝试了以下内容:

DateTime firstDay = GetFirstDayOfFirstWeekOfYear();
var userTimes = from t in context.TrackedTimes
                group t by new {t.User.UserName, WeekNumber = (t.TargetDate - firstDay).Days / 7} into ut
                select new
                {
                    UserName = ut.Key.UserName,
                    WeekNumber = ut.Key.WeekNumber,
                    Minutes = ut.Sum(t => t.Minutes)
                };
Run Code Online (Sandbox Code Playgroud)

但LINQ to Entities似乎不支持DateTime对象的算术运算,因此它不知道该怎么做(t.TargetDate - firstDay).Days / 7.

我考虑在数据库中创建一个只需几天到几周的视图,然后将该视图添加到我的实体框架上下文并在我的LINQ查询中加入它,但这似乎是很多工作.算术方法有一个很好的解决方法吗?与Linq to Entities一起使用的东西,我可以简单地将其合并到LINQ语句中而无需触及数据库?某种方式告诉Linq实体如何从另一个中减去一个日期?

摘要

我要感谢大家的深思熟虑和创造性的回应.在所有这些来回之后,看起来这个问题的真正答案是"等到.NET 4.0." 我将向Noldorin提供奖励,以提供仍然利用LINQ的最实用的答案,特别提到Jacob Proffitt提出的使用实体框架的响应,而无需在数据库端进行修改.还有其他很好的答案,如果你是第一次看到这个问题,我强烈建议你阅读所有已被投票的人,以及他们的评论.这确实是StackOverflow功能的极好例子.谢谢你们!

Nol*_*rin 40

您应该能够使用对AsEnumerable扩展方法的调用来强制查询使用LINQ to Objects而不是LINQ to Entities进行分组.

请尝试以下方法:

DateTime firstDay = GetFirstDayOfFirstWeekOfYear();
var userTimes = 
    from t in context.TrackedTimes.Where(myPredicateHere).AsEnumerable()
    group t by new {t.User.UserName, WeekNumber = (t.TargetDate - firstDay).Days / 7} into ut
    select new
    {
        UserName = ut.Key.UserName,
        WeekNumber = ut.Key.WeekNumber,
        Minutes = ut.Sum(t => t.Minutes)
    };
Run Code Online (Sandbox Code Playgroud)

这至少意味着该where子句由LINQ to Entities执行,但该group子句对于实体来说太复杂了,它由LINQ to Objects完成.

如果你对此有任何好运,请告诉我.

更新

这是另一个建议,它可能允许您使用LINQ to Entities来完成整个事情.

(t.TargetDate.Days - firstDay.Days) / 7
Run Code Online (Sandbox Code Playgroud)

这只是扩展了操作,因此只执行整数减法而不是DateTime减法.

它目前尚未经过测试,因此它可能有效也可能无效......


HBl*_*rby 8

只要您使用SQLServer,您也可以在LINQ中使用SQL函数:

using System.Data.Objects.SqlClient;

var codes = (from c in _twsEntities.PromoHistory
                         where (c.UserId == userId)
                         group c by SqlFunctions.DatePart("wk", c.DateAdded) into g
                         let MaxDate = g.Max(c => SqlFunctions.DatePart("wk",c.DateAdded))
                         let Count = g.Count()
                         orderby MaxDate
                         select new { g.Key, MaxDate, Count }).ToList();
Run Code Online (Sandbox Code Playgroud)

此示例改编自MVP Zeeshan Hirani在此主题: MSDN


Sam*_*ill 6

我的时间跟踪应用程序中确实存在这个问题,需要按周报告任务.我尝试了算术方法但没有得到任何合理的工作.我最终只是在数据库中添加一个新列,其中包含周数,并为每个新行计算.突然间,所有的痛苦都消失了.我讨厌这样做,因为我是DRY的忠实信徒,但我需要取得进步.不是你想要的答案,而是另一个用户的观点.我会看到这个得到一个体面的答案.