c# – 使用DBFunction或替代方法的EntityFramework GroupBy

我有100行不同日期的数据.我希望将结果按每30分钟相同的日期进行分组.

代替:
结果1,2016-02-02 13:00:24
结果1,2016-02-02 13:01:24
结果1,2016-02-02 13:02:24
结果1,2016-02-02 13:33:24

需要:

结果1,2016-02-02 13:00:24
结果1,2016-02-02 13:33:24

原始查询:

    return await loc.Where(p => p.ReadTime >= df && p.ReadTime <= dt)
        .OrderBy(p => p.ReadTime)
        .ProjectTo<LocationModel>().ToListAsync();

我尝试了什么:

    return await loc.Where(p => p.ReadTime >= df && p.ReadTime <= dt)
        .GroupBy(p => DbFunctions.TruncateTime(p.ReadTime))
        .Select(p => new LocationModel
        {
            Lng = p.FirstOrDefault().Lng,
            Lat = p.FirstOrDefault().Lat
        })
        .ToListAsync();

堆栈跟踪错误:

.ThrowForNonSuccess(Task task)\r\n at
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification
(Task task)\r\n at
System.Runtime.CompilerServices.TaskAwaiter1.GetResult()\r\n at
System.Web.Http
.Dispatcher.HttpControllerDispatcher.<SendAsync>d__1.MoveNext()","innerException":{"message":"An
error has occurred.","exceptionMessage":"Timeout expired. The
timeout period elapsed prior to completion of the operation or the
server is not
responding.","exceptionType":"System.Data.SqlClient.SqlException"
,"stackTrace":" at
System.Data.SqlClient.SqlCommand.<>c.<ExecuteDbDataReaderAsync>b__167_0(Task
1
result )\r\n at
System.Threading.Tasks.ContinuationResultTaskFromResultTask`2.InnerInvoke()\r\n
at System .Threading.Tasks.Task.Execute()\r\n— End of stack trace
from previous location where exception was thrown —\r\n at
System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task
task)\r\n at
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task
task)\r \n at
System.Data.Entity.Core.EntityClient.Internal.EntityCommandDefinition.

d__c.MoveNext()”,”innerException”:{“message”:”An error has occurred.”,”exceptionMessage”:”The wait operation timed
out”,”exceptionType”:”System.ComponentModel.Win32Exception”,”stackTrace”:null}}}

最佳答案 除了将所有数据加载到内存中并根据需要手动格式化之外,我可以想到用单个查询执行此操作的唯一可行方法是将行放入预定义日期“桶”并选择一行从每个桶.

桶的最合乎逻辑的选择是在您寻找30分钟间隔后的小时和半小时,例如:

Result 1, 2016-02-02 13:00:24, 2016-02-02 13:00:00 (bucket 1)
Result 1, 2016-02-02 13:01:24, 2016-02-02 13:00:00 (bucket 1)
Result 1, 2016-02-02 13:02:24, 2016-02-02 13:00:00 (bucket 1)
Result 1, 2016-02-02 13:33:24, 2016-02-02 13:30:00 (bucket 2)

要解决的下一个问题是选择每个桶使用哪一行.您可以使用的一种方法是确定最接近桶的日期(最小值)并使用它:

from l in loc
join bucket in (
    from l in loc
    group l by new DateTime(l.ReadTime.Year, l.ReadTime.Month, l.ReadTime.Day, l.ReadTime.Hour, l.ReadTime.Minute < 30 ? 0 : 30, 0) into g
    select g.Min(m => m.ReadTime)
) on l.ReadTime equals bucket
select new LocationModel
{
    Lng = l.FirstOrDefault().Lng,
    Lat = l.FirstOrDefault().Lat
}

需要注意这种方法,但如果存在具有相同日期/时间值的行,则最终可能会出现重复.如果实体/表使用标识列作为其主键,并且以日期/时间顺序方式将行插入到表中,则可以使用该键来确定每个存储桶的行,因为该键实际上是按升序日期顺序排列的:

from l in loc
join bucket in (
    from l in loc
    group l by new DateTime(l.ReadTime.Year, l.ReadTime.Month, l.ReadTime.Day, l.ReadTime.Hour, l.ReadTime.Minute < 30 ? 0 : 30, 0) into g
    select g.Min(m => m.Id)
) on l.Id equals bucket
select new LocationModel
{
    Lng = l.FirstOrDefault().Lng,
    Lat = l.FirstOrDefault().Lat
}

这将确保没有重复.否则,您需要在代码中过滤重复项或进一步扩展查询.我对你的特定领域了解不多,所以我会留下这部分给你解决.

我还应该指出Linq to SQL支持上述查询,但是如果不支持某些内容(例如DbFunctions.CreateDateTime),则可能需要将DbFunctions类用于带有EF的DateTime位.

点赞