Linq日期范围查询无法返回正确数据的技术问题
问题描述
我编写了一段用于按日期范围列出数据的Linq查询代码,具体实现如下:
[HttpPost] public JsonResult AjaxMethod(string search, DateTime startDate, DateTime endDate) { var stDate = startDate.Date; var enDate = endDate.Date; var str = search; List<LoanAccount> loanAccounts = (from loanCustomer in db.LoanAccounts where (loanCustomer.Account_Number.Contains(search) || search == null) && (loanCustomer.Date_Opened >= stDate && loanCustomer.Date_Opened <= enDate) select loanCustomer).ToList(); return Json(loanAccounts); }
实际运行时发现,当stDate为23/01/2021且enDate为23/01/2021时,无法返回当天的数据;若将enDate修改为24/01/2021,则能返回2021年1月23日的数据,但不会包含2021年1月24日的数据。请问我是否遗漏了某些关键处理步骤?
问题原因
这个问题我之前也碰到过,核心是**DateTime.Date属性的截断行为**在搞鬼:
当你调用startDate.Date和endDate.Date时,会把原DateTime的时间部分强制设置为00:00:00。比如你传入的endDate是2021-01-23,转换后enDate实际是2021-01-23 00:00:00。
而数据库中Date_Opened字段的记录大概率包含具体时间(比如2021-01-23 09:30:15),此时loanCustomer.Date_Opened <= enDate的判断结果是false——因为09:30:15晚于00:00:00,自然查不到当天的数据。
当你把enDate改成2021-01-24时,enDate变成2021-01-24 00:00:00,23号当天所有带时间的记录都小于这个值,所以能被查到;但24号的记录只有时间为00:00:00的才会满足<=,其他时间的都会被排除,所以看不到24号的数据。
解决方案
这里提供两种可靠的修复方式,你可以根据自己的场景选择:
方案1:直接比较日期部分
把查询条件改为只对比Date_Opened的日期部分,忽略时间:
List<LoanAccount> loanAccounts = (from loanCustomer in db.LoanAccounts where (string.IsNullOrEmpty(search) || loanCustomer.Account_Number.Contains(search)) && (loanCustomer.Date_Opened.Date >= stDate && loanCustomer.Date_Opened.Date <= enDate) select loanCustomer).ToList();
注意:如果使用Entity Framework,这种写法会被正确转换为SQL的日期比较,不会有性能问题;但部分小众ORM可能需要额外处理,建议测试验证。
方案2:调整结束日期的上限(更推荐)
把结束日期设置为原结束日期的次日零点,然后用<代替<=来匹配范围:
[HttpPost] public JsonResult AjaxMethod(string search, DateTime startDate, DateTime endDate) { var stDate = startDate.Date; // 将结束日期改为次日零点,确保包含原结束日期当天的所有时间 var enDate = endDate.Date.AddDays(1); List<LoanAccount> loanAccounts = (from loanCustomer in db.LoanAccounts where (string.IsNullOrEmpty(search) || loanCustomer.Account_Number.Contains(search)) && (loanCustomer.Date_Opened >= stDate && loanCustomer.Date_Opened < enDate) select loanCustomer).ToList(); return Json(loanAccounts); }
这种写法的优势在于:
- 完全利用数据库的DateTime索引,性能最优
- 逻辑清晰,不会因为时间部分的差异出现遗漏
- 同时修复了原代码中
search == null的漏洞(改为string.IsNullOrEmpty(search),避免空字符串导致过滤条件失效)
内容的提问来源于stack exchange,提问作者uthumvc

