在LINQ to Entities中按周分组

c# entity-framework linq linq-to-entities

我有一个应用程序,允许用户输入他们工作的时间,我正在尝试为此构建一个良好的报告,利用LINQ to Entities。因为每个TrackedTime都有一个TargetDate ,它只是DateTime的“Date”部分,所以按用户和日期对时间进行分组相对简单(为简单起见,我省略了“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)
                };

由于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)
                };

现在是棘手的部分。是否有可靠的方式按周分组?我根据对类似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)
                };

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

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

摘要

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

一般承认的答案

您应该能够使用对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)
    };

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

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

更新

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

(t.TargetDate.Days - firstDay.Days) / 7

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

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


热门答案

只要您使用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();

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



许可下: CC-BY-SA with attribution
不隶属于 Stack Overflow
这个KB合法吗? 是的,了解原因
许可下: CC-BY-SA with attribution
不隶属于 Stack Overflow
这个KB合法吗? 是的,了解原因