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
相关产品推荐
相关产品推荐

