如何使用DbFunctions保留时分实现LINQ to Entities DateTime比较?
嘿,我完全懂你碰到的这个麻烦——LINQ to Entities没办法把ToString()这类.NET方法转换成对应的SQL表达式,所以直接用字符串比较肯定会报错。咱们换个更靠谱的思路:直接对DateTime值进行精确到分钟的截断处理,这样EF就能正确解析并生成对应的SQL查询了。
为什么字符串比较行不通?
LINQ to Entities does not recognize the method 'System.String ToString(System.String)' method, and this method cannot be translated into a store expression.
这个错误的核心原因是:EF需要把你写的LINQ查询转换成数据库能理解的SQL语句,但ToString("MM/dd/yyyy HH:mm")是.NET专属的方法,数据库里没有对应的等价函数,所以EF没法完成这个转换。
正确的解决方案:直接操作DateTime的时间部分
方案1:利用时间范围查询(推荐,性能更优)
既然要匹配到分钟,咱们可以计算出目标时间的分钟起始点和下一分钟的起始点,然后查询scan_datetime落在这个区间内的记录。这种方式还能利用数据库中scan_datetime字段的索引,查询效率更高。
// 先把目标时间截断到分钟 var targetTime = dt_scan.s.ScanDatetime.Date.AddHours(dt_scan.s.ScanDatetime.Hour) .AddMinutes(dt_scan.s.ScanDatetime.Minute); // 计算下一分钟的起始时间 var nextMinute = targetTime.AddMinutes(1); var seekScan = (from s in aDb.Item_Scan_Event where s.item_detail_id == th_piece_id && s.scan_type == scantype && s.scan_datetime >= targetTime && s.scan_datetime < nextMinute select s).FirstOrDefault();
方案2:用数据库函数截断到分钟(EF6及更早版本)
如果你用的是EF6,可以借助DbFunctions类里的方法来对scan_datetime进行分钟截断,和目标时间做对比:
var targetTime = dt_scan.s.ScanDatetime; var seekScan = (from s in aDb.Item_Scan_Event where s.item_detail_id == th_piece_id && s.scan_type == scantype && DbFunctions.CreateDateTime( DbFunctions.DatePart("year", s.scan_datetime), DbFunctions.DatePart("month", s.scan_datetime), DbFunctions.DatePart("day", s.scan_datetime), DbFunctions.DatePart("hour", s.scan_datetime), DbFunctions.DatePart("minute", s.scan_datetime), null) == targetTime.Date.AddHours(targetTime.Hour).AddMinutes(targetTime.Minute) select s).FirstOrDefault();
方案3:EF Core中的简化写法
如果是EF Core,直接用DateTime的Truncate扩展方法(或者手动计算),EF Core能直接转换成对应的SQL:
var targetTime = dt_scan.s.ScanDatetime.Truncate(TimeSpan.FromMinutes(1)); var seekScan = (from s in aDb.Item_Scan_Event where s.item_detail_id == th_piece_id && s.scan_type == scantype && EF.Functions.DateDiffMinute(s.scan_datetime, targetTime) == 0 select s).FirstOrDefault();
或者用时间范围的方式,和方案1一样,EF Core也能很好地支持。
总结
优先选方案1的时间范围查询,因为它不仅能被EF正确转换,还能利用索引提升查询性能,避免全表扫描。字符串转换的方式在LINQ to Entities里基本走不通,咱们直接操作DateTime的时间部分才是正确的路子~
内容的提问来源于stack exchange,提问作者Joe Ruder

