C# 在IQueryable中按小时分组
在我的项目中,我在x秒内从SPS接收到一些数据。每y分钟我都会将当前数据归档到数据库中,以便能够显示统计数据 我收到的数据被放入一个模型中。类似这样但更复杂的事情: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; } } 我有一个数据库存储库,它返回特定时间范
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));