【发布时间】:2019-11-24 01:42:08
【问题描述】:
在我的项目中,我在所有 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));
如您所见,这会返回 IQueryable,因此我想利用 linq to entity 功能。
我相信它被称为 linq to entity,但如果不是,我的意思是将表达式树转换为 sql 或其他内容的功能,而不仅仅是在 C# 中执行它。
由于数据库中每小时的条目数量无法确定,我希望每小时只获得一个条目(第一个),以便将其显示在图表中。
以下是一些日期时间的示例,可能更能说明我的意图:
注意:这些只是对象中包含的日期时间,我想要整个对象 - 而不仅仅是时间.
// say this is all the data I get between two times
2019-07-06 10:30:01 // I want
2019-07-06 10:40:09
2019-07-06 10:50:10
2019-07-06 11:00:13 // I want
2019-07-06 11:10:20
2019-07-06 11:20:22
2019-07-06 11:30:24
2019-07-06 11:40:32
2019-07-06 11:50:33
2019-07-06 12:00:35 // I want
2019-07-06 12:10:43
2019-07-06 12:20:45
2019-07-06 12:40:54
2019-07-06 12:50:56
2019-07-06 13:00:58 // I want
2019-07-06 13:11:06
2019-07-06 13:21:08
2019-07-06 13:31:09
我目前的做法是通过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());
这很好用,但由于它使用IEnumerable 并创建对象,我没有获得 linq 到实体的优势,我认为这种方式必须慢很多。
有没有办法重写这个查询以在 SQLite 数据库上使用IQueryable?
编辑:我正在使用 .net core 3 preview6(最新预览版)版本的 EF Core。也许有一个新功能可以满足我的需求:)
【问题讨论】:
-
我猜你在没有 AsEnumerable 的情况下尝试过它,但它不起作用,这可能是因为 LINQ 提供程序无法真正将
new DateTime(...)或First(...)的分组转换为 SQL。但是,我认为dataLastWeek.GroupBy(d => new { d.ArchiveTime.Year, d.ArchiveTime.Month, d.ArchiveTime.Day, d.ArchiveTime.Hour }).Select(g => g.Min(x => x.ArchiveTime));应该可以工作,因为这可以在 SQL 中表示为SELECT MIN(ArchiveTime) FROM data GROUP BY DATEPART(YEAR, ArchiveTime), DATEPART(MONTH, ArchiveTime), DATEPART(DAY, ArchiveTime), DATEPART(HOUR, ArchiveTime)。 -
是的,我确实尝试过(没有
AsEnumerable),你说得对,这不起作用。此外,您建议的解决方案仅返回DateTime,而不是Data-object 本身:/ -
AsEnumerable()在 EF Core 中不需要。不幸的是,对于当前的 EF Core 查询翻译,由于First()或FirstOrDefault(),查询将使用客户端评估。 -
@IvanStoev 如果我不使用
AsEnumerable(),它认为我想在IQueryable上执行where,这会引发异常,因为我创建了一个新的DateTime,EF 将如何知道该怎么做。有什么办法可以删除第一个呢? @juharr 提到了OrderBy,但在这种情况下你会如何使用它呢?Take(1)? -
@ckuri 我编辑了问题以添加一个注释,我想要完整的对象,而不仅仅是日期时间。您的解决方案似乎工作正常,但如前所述,只返回日期时间。
标签: c# datetime entity-framework-core linq-to-entities iqueryable