C# 在IQueryable中按小时分组

C# 在IQueryable中按小时分组,c#,datetime,entity-framework-core,linq-to-entities,iqueryable,C#,Datetime,Entity Framework Core,Linq To Entities,Iqueryable,在我的项目中,我在x秒内从SPS接收到一些数据。每y分钟我都会将当前数据归档到数据库中,以便能够显示统计数据 我收到的数据被放入一个模型中。类似这样但更复杂的事情: public class Data { public DateTime ArchiveTime { get; set; } public float TempC { get; set; } public float CO2Percent { get; set; } } 我有一个数据库存储库,它返回特定时间范

在我的项目中,我在x秒内从SPS接收到一些数据。每y分钟我都会将当前数据归档到数据库中,以便能够显示统计数据

我收到的数据被放入一个模型中。类似这样但更复杂的事情:

public class Data
{
    public DateTime ArchiveTime { get; set; }
    public float TempC { get; set; }
    public float CO2Percent { get; set; }
}
我有一个数据库存储库,它返回特定时间范围内的所有条目。请参阅此代码:

// Context is my DbContext for a SQLite db and Data is the DbSet<Data> on that
IQueryable<Data> GetDataBetween(DateTime from, DateTime to) => Context.Data.Where(d => (d.ArchiveTime >= from && d.ArchiveTime <= to));
目前我这样做的方式是通过
IEnumerable
GroupBy
。请参阅此代码:

var now = DateTime.Now;
IQueryable<Data> dataLastWeek = repos.GetDataBetween(now.AddDays(-7), now);

IEnumerable<Data> onePerHour = dataLastWeek.AsEnumerable()
    .GroupBy(d => new DateTime(d.ArchiveTime.Year, d.ArchiveTime.Month, d.ArchiveTime.Day, d.ArchiveTime.Hour, 0, 0))
    .Select(g => g.First());
var now=DateTime.now;
IQueryable dataLastWeek=repos.GetDataBetween(now.AddDays(-7),now);
IEnumerable onePerHour=dataLastWeek.AsEnumerable()
.GroupBy(d=>newdatetime(d.ArchiveTime.Year,d.ArchiveTime.Month,d.ArchiveTime.Day,d.ArchiveTime.Hour,0,0))
.选择(g=>g.First());
这很好,但是因为它使用了
IEnumerable
并创建了对象,所以我没有得到linq对实体的好处,而且我认为这样做的速度一定要慢得多

有没有办法重写此查询以在SQLite数据库上使用
IQueryable


编辑:我正在使用.NETCore3Preview6(最新预览)版本的EFCore。也许有一个新功能可以满足我的需要:)

通过避免
新的日期时间(…)
并使用任何一种匿名类型,可以轻松地将
GroupBy的关键部分转换为可翻译的

.GroupBy(d => new { d.ArchiveTime.Date, d.ArchiveTime.Hour })
Date
property+
AddHours

.GroupBy(d => d.ArchiveTime.Date.AddHours(d.ArchiveTime.Hour))
不幸的是,目前(EF Core 2.2)没有将嵌套的
First
/
FirstOrDefault
/
Take(1)
转换为SQL,并使用客户机评估。对于
First()

对于具体的查询,我看到的唯一服务器端解决方案是根本不使用
GroupBy
,而是使用相关的自反连接,如下所示:

var onePerHour = dataLastWeek.Where(d => !dataLastWeek.Any(d2 =>
    d2.ArchiveTime.Date == d.ArchiveTime.Date &&
    d2.ArchiveTime.Hour == d.ArchiveTime.Hour &&
    d2.ArchiveTime < d.ArchiveTime));
var onePerHour=dataLastWeek.Where(d=>!dataLastWeek.Any(d2=>
d2.ArchiveTime.Date==d.ArchiveTime.Date&&
d2.ArchiveTime.Hour==d.ArchiveTime.Hour&&
d2.ArchiveTime
我猜您在没有AsEnumerable的情况下尝试了它,但它不起作用,这可能是因为LINQ提供程序无法将分组的
新日期时间(…)
第一个(…)
转换为SQL。但是,我认为
dataLastWeek.GroupBy(d=>new{d.ArchiveTime.Year,d.ArchiveTime.Month,d.ArchiveTime.Day,d.ArchiveTime.Hour})应该可以工作,因为它可以用SQL表示为
从数据组中按日期部分(年,存档时间),日期部分(月,存档时间),日期部分(日,存档时间),日期部分(小时,存档时间)
。是的,我尝试过(没有
可计算的
)选择MIN(存档时间),您可以正确地认为这不起作用。另外,您建议的解决方案只返回
日期时间
,而不返回
数据
-对象本身:/
AsEnumerable()
在EF Core中不需要。不幸的是,对于当前的EF核心查询转换,查询仍将使用客户端求值,因为如果我不使用
AsEnumerable(),则会使用
First()
FirstOrDefault()
@IvanStoev()
它认为我想在
IQueryable
上执行
where
,这会抛出一个异常,因为我创建了一个新的
DateTime
,EF如何知道如何执行该操作。那么,有没有办法去掉第一个呢@juharr提到了OrderBy,但在本文中,您将如何使用它?通过
Take(1)
?@ckuri,我编辑了这个问题,添加了一个注释,说明我想要的是完整的对象,而不仅仅是日期时间。您的解决方案似乎工作正常,但如上所述,只返回日期时间。这很有效,谢谢。然而,你只检查年份和小时,有什么原因吗?要获得我想要的行为(以及旧解决方案的行为),您还需要逐月逐日检查,否则您只能获得24个条目。非常快。。我不直接将数据用于图形,而是从中创建元组。这些对ef来说是未知的,因此如果我在
IQueryable
上执行
select
,它将抛出。当我使用
AsEnumerable
时,它可以工作,但速度非常慢。使用
ToList()
而不是
AsEnumerable
将其速度提高约3倍。在我创建元组之前,每小时调用一次
ToList()
是否正确?(1)对不起,复制/粘贴错误,我的意思是
Date
(2)不确定你的意思,
ToList()
加载内存中的所有内容。我已经问过了。如果您有时间调查一下,我将不胜感激:)
var onePerHour = dataLastWeek.Where(d => !dataLastWeek.Any(d2 =>
    d2.ArchiveTime.Date == d.ArchiveTime.Date &&
    d2.ArchiveTime.Hour == d.ArchiveTime.Hour &&
    d2.ArchiveTime < d.ArchiveTime));