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

C# Linq Expression生成SQL时DateTime格式错误的解决方法

问题描述

我有一个泛型类,需要将构造函数参数转换为DateTime?类型:

var startDate = CompareValueFirst == null ? (DateTime?)null : Convert.ToDateTime(CompareValueFirst);
var endDate = CompareValueSecond == null ? (DateTime?)null : Convert.ToDateTime(CompareValueSecond);

随后构建Linq Expression:

var startDateRight = startDate != null ? Expression.Constant(new DateTime(startDate.Value.Year, startDate.Value.Month, startDate.Value.Day, 0, 0, 0), typeof(DateTime?)) : Expression.Constant(startDate);
var endDateRight = endDate != null ? Expression.Constant(new DateTime(endDate.Value.Year, endDate.Value.Month, endDate.Value.Day, 23, 59, 59), typeof(DateTime?)) : Expression.Constant(endDate);

基于上述日期对象构建表达式:

var parameter = Expression.Parameter(typeof(T), "x");
var left = propertyName.Split('.').Aggregate((Expression)parameter, Expression.Property);
var expression = Expression.AndAlso(
    Expression.MakeBinary(ExpressionType.GreaterThanOrEqual, left, startDateRight),
    Expression.MakeBinary(ExpressionType.LessThanOrEqual, left, endDateRight));

最终生成表达式并查询:

Expression<Func<T, bool>> ExpressionResult;
ExpressionResult = Expression.Lambda<Func<T, bool>>(expression, parameter);
var results = list.Where(ExpressionResult);

执行时数据库返回错误:

Conversion failed when converting date and/or time from character string.

原因是生成的SQL中DateTime格式为:

'2022-08-25T00:00:00.0000000'

而数据库要求格式应为:

'2022-08-25 00:00:00'
解决方案

核心问题不是手动控制字符串格式,而是要让ORM(如EF/EF Core)以数据库兼容的方式参数化DateTime值,避免直接将日期转成字符串嵌入SQL。以下是可行的修改方案:

方案1:使用参数化表达式而非Constant直接嵌入

避免用Expression.Constant直接传入DateTime值,改用参数化方式让ORM自动处理格式兼容:

// 定义日期参数
var startParam = Expression.Parameter(typeof(DateTime?), "start");
var endParam = Expression.Parameter(typeof(DateTime?), "end");

// 构建表达式时使用参数
var expression = Expression.AndAlso(
    Expression.MakeBinary(ExpressionType.GreaterThanOrEqual, left, startParam),
    Expression.MakeBinary(ExpressionType.LessThanOrEqual, left, endParam));

// 编译时传入实际日期值
var lambda = Expression.Lambda<Func<T, DateTime?, DateTime?, bool>>(expression, parameter, startParam, endParam);
var results = list.Where(item => lambda.Compile()(item, startDate, endDate));

这种方式会生成参数化SQL,完全规避字符串格式不兼容的问题。

方案2:截断DateTime精度并优化Constant表达式

如果必须使用Expression.Constant,先截断日期的毫秒部分,减少格式转换风险:

// 处理日期,去掉毫秒部分
var startDateTruncated = startDate?.Date.AddHours(0);
var endDateTruncated = endDate?.Date.AddHours(23).AddMinutes(59).AddSeconds(59);

// 构建Constant表达式时使用截断后的日期
var startDateRight = startDateTruncated != null 
    ? Expression.Constant(startDateTruncated, typeof(DateTime?)) 
    : Expression.Constant(null, typeof(DateTime?));
var endDateRight = endDateTruncated != null 
    ? Expression.Constant(endDateTruncated, typeof(DateTime?)) 
    : Expression.Constant(null, typeof(DateTime?));

针对EF Core,还可以使用内置日期函数比较日期部分,彻底绕开格式问题:

// 用DateDiffDay比较日期区间
var startDateExpr = Expression.Constant(startDate.Value.Date, typeof(DateTime));
var endDateExpr = Expression.Constant(endDate.Value.Date, typeof(DateTime));

// 构建"大于等于起始日期"的条件
var startCondition = Expression.MakeBinary(
    ExpressionType.GreaterThanOrEqual,
    Expression.Call(
        typeof(EF), 
        nameof(EF.Functions.DateDiffDay), 
        null, 
        left, 
        startDateExpr),
    Expression.Constant(0));

// 构建"小于等于结束日期"的条件
var endCondition = Expression.MakeBinary(
    ExpressionType.LessThanOrEqual,
    Expression.Call(
        typeof(EF), 
        nameof(EF.Functions.DateDiffDay), 
        null, 
        left, 
        endDateExpr),
    Expression.Constant(0));

var expression = Expression.AndAlso(startCondition, endCondition);

方案3:修改数据库兼容设置(不推荐)

如果数据库是SQL Server,可修改配置兼容ISO格式:

SET DATEFORMAT ymd;
SET LANGUAGE us_english;

但该方式依赖数据库配置,通用性差,不建议作为首选方案。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 01:15:40