You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

EF Core中合并Date与毫秒数并解决日期溢出问题

问题:合并Date与BigInt类型字段判断日期重叠时的EF Core SQL生成问题

背景

我需要检查两个日期是否重叠,但数据库表未使用datetime类型,而是拆分用date和bigint类型存储时间信息,因此得在C#代码中将它们合并为DateTime进行判断。

数据库表列

  • StartDate: date(可为空)
  • EndDate: date(可为空)
  • AvailableFromMs: bigint(可为空)
  • AvailableToMs: bigint(可为空)

实体类TEntity

[Column(TypeName = "date")]
public DateTime? StartDate { get; set; }
[Column(TypeName = "date")]
public DateTime? EndDate { get; set; }
public TimeSpan? AvailableFromMs { get; set; }
public TimeSpan? AvailableToMs { get; set; }

当前查询代码

IQueryable<TEntity> query = <repository_to_query_from>...

DateTime routeStart = DateTime.Now;

List<TEntity> overlapping = query
    .Where(r => routeStart <= ((DateTime)(object)r.EndDate.Value).AddSeconds(r.AvailableToMs == null ? 0.0 : r.AvailableToMs.Value.Milliseconds / 1000.0))
    .ToList();

注:我知道这只是条件的一部分,等EF生成正确的SQL后我会更新它

EF Core生成的SQL

WHERE...
  (@__routeStart_1 <= DATEADD(second, CAST(CASE
 WHEN [r].[AvailableToMs] IS NULL THEN 0.0E0
 ELSE CAST(DATEPART(millisecond, [r].[AvailableToMs]) AS float) / 1000.0E0
 END AS int), CAST([r].[EndDate] AS datetime2)))

执行错误

执行查询时抛出错误:

Arithmetic overflow error converting expression to data type datetime.

问题原因

AvailableToMs的类型处理逻辑不符合预期:C#代码中是先将毫秒数除以1000转成秒,但EF生成的SQL却是先把DATEPART取到的毫秒值转成float再除以1000,之后才转成int传给DATEADD。这种顺序导致了溢出,我需要让EF先执行除法,再转成int。


解决方案

方案1:修正TimeSpan属性的取值逻辑

之前代码中使用Milliseconds是错误的,它仅取TimeSpan中0-999的毫秒部分,而数据库存储的是总毫秒数。应改用TotalMilliseconds,并采用整数除法避免浮点转换:

List<TEntity> overlapping = query
    .Where(r => routeStart <= r.EndDate.Value.AddSeconds(r.AvailableToMs == null ? 0 : r.AvailableToMs.Value.TotalMilliseconds / 1000))
    .ToList();

这种写法会让EF生成先做除法、再转int的SQL,避免转换顺序问题。

方案2:使用EF.Functions直接控制SQL结构

如果方案1仍不生效,可通过EF.Functions直接构造DateAdd逻辑,强制按预期顺序执行:

List<TEntity> overlapping = query
    .Where(r => routeStart <= EF.Functions.DateAdd(
        Microsoft.EntityFrameworkCore.SqlServer.DateParts.Second,
        r.AvailableToMs == null ? 0 : (int)(r.AvailableToMs.Value.TotalMilliseconds / 1000),
        r.EndDate.Value))
    .ToList();

方案3:修正实体类属性类型(推荐)

数据库中AvailableFromMs和AvailableToMs是bigint类型,存储的是总毫秒数,实体类用TimeSpan?映射易导致解析错误。应改为long?类型,匹配数据库存储逻辑:

[Column(TypeName = "date")]
public DateTime? StartDate { get; set; }
[Column(TypeName = "date")]
public DateTime? EndDate { get; set; }
public long? AvailableFromMs { get; set; }
public long? AvailableToMs { get; set; }

对应的查询代码:

List<TEntity> overlapping = query
    .Where(r => routeStart <= r.EndDate.Value.AddSeconds(r.AvailableToMs == null ? 0 : r.AvailableToMs.Value / 1000))
    .ToList();

这种类型匹配更直接,EF会生成完全符合预期的SQL:先对bigint值做除法,再转int传给DATEADD,彻底解决溢出问题。

内容的提问来源于stack exchange,提问作者T Sl

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 08:30:40