简体   繁体   English

EntityFramework GroupBy与DBFunctions或替代

[英]EntityFramework GroupBy with DBFunctions or alternative

I have 100 rows of data with different dates. 我有100行不同日期的数据。 I would like to group by the result by same dates each 30 minutes apart. 我希望将结果按每30分钟相同的日期进行分组。

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

Needed: 需要:

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

Original query: 原始查询:

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

What I tried: 我尝试了什么:

    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();

Stack trace error: 堆栈跟踪错误:

.ThrowForNonSuccess(Task task)\\r\\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification (Task task)\\r\\n at System.Runtime.CompilerServices.TaskAwaiter 1.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 .ThrowForNonSuccess(任务任务)\\ r \\ n在System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(任务任务)\\ r \\ n在System.Runtime.CompilerServices.TaskAwaiter 1.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.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. 1.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结果)\\ r \\ n在System.Threading.Tasks.ContinuationResultTaskFromResultTask`2.InnerInvoke()\\ r \\ n在System .Threading.Tasks.Task.Execute()\\ r \\ n-- - 从抛出异常的先前位置开始的堆栈跟踪结束---在System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(任务任务)\\ r \\ n处于System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(任务任务)的\\ r \\ n )\\ r \\ n在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}}} d__c.MoveNext()“,”innerException“:{”message“:”发生错误。“,”exceptionMessage“:”等待操作超时“,”exceptionType“:”System.ComponentModel.Win32Exception“,”stackTrace “:空值}}}

Other than loading all of the data in to memory and manually formatting it as required, the only feasible way I can think of to do this with a single query is to put the rows in to pre-defined date "buckets" and choose a row from each bucket. 除了将所有数据加载到内存中并根据需要手动格式化之外,我可以想到用单个查询执行此操作的唯一可行方法是将行放入预定义日期“桶”并选择一行从每个桶。

The most logical choice for the buckets would be on the hour and half past the hour since you're looking for 30 minute intervals, for example: 桶的最合乎逻辑的选择是在您寻找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)

The next problem to tackle is selecting which row to use for each of the buckets. 要解决的下一个问题是选择每个桶使用哪一行。 One approach you could use is to determine the date closest to the bucket (minimum) and use it: 您可以使用的一种方法是确定最接近桶的日期(最小值)并使用它:

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
}

Care needs to be taken with this approach though as you could end up with duplicates if there are rows with identical date/time values. 需要注意这种方法,但如果存在具有相同日期/时间值的行,则最终可能会出现重复。 If the entity/table uses an identity column as its primary key and rows are inserted in to the table in a date/time sequential manner, you can use the key to determine the row per bucket as the key is effectively in ascending date order: 如果实体/表使用标识列作为其主键,并且以日期/时间顺序方式将行插入到表中,则可以使用该键来确定每个存储桶的行,因为该键实际上是按升序日期顺序排列的:

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
}

This will ensure there are no duplicates. 这将确保没有重复。 Otherwise you'll either need to filter the duplicates in code or extend the query further. 否则,您需要在代码中过滤重复项或进一步扩展查询。 I don't know enough about your particular domain though so I'll leave this part for you to solve. 我对你的特定领域了解不多,所以我会留下这部分给你解决。

I should also point out the above queries are supported by Linq to SQL, however you may need to use the DbFunctions class for the DateTime bits with EF if something isn't supported (eg DbFunctions.CreateDateTime). 我还应该指出Linq to SQL支持上述查询,但是如果不支持某些内容(例如DbFunctions.CreateDateTime),您可能需要将DbFunctions类用于带有EF的DateTime位。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM