Expression.Convert() 对于 EF Core 中的日期时间数据类型无法正常工作

Expression.Convert() not working correctly for datetime datatype in EF Core

在 .Net 标准 2.0 项目中,我使用 Microsoft.EntityFrameworkCore nuget 版本 2.2.6。我正在动态创建 lambda 表达式,如下所示:

//EntityUtility.ConvertToType() converts string to DateTime type
var constant1 = Expression.Constant(EntityUtility.ConvertToType(minVal, propertyInfo.PropertyType));
var constant2 = Expression.Constant(EntityUtility.ConvertToType(maxVal, propertyInfo.PropertyType));
if (Nullable.GetUnderlyingType(member.Type) != null)
{
   member = Expression.Property(member, "Value");
}

var exMin = Expression.GreaterThanOrEqual(member, Expression.Convert(constant1, propertyInfo.PropertyType));
var exMax = Expression.LessThanOrEqual(member, Expression.Convert(constant2, propertyInfo.PropertyType));
operation = Expression.And(exMax, exMin);

查询执行后失败并抛出错误消息 Conversion failed when converting date and/or time from character string.。它在 SQL 中转换为错误的日期时间格式。以下是日期时间输入值的转换 SQL 示例:

SELECT [user].[username]
FROM [dbo].[user] AS [user]
WHERE (CASE
    WHEN [user].[update_date] <= '2018-03-24T00:00:01.0000000'
    THEN CAST(1 AS BIT) ELSE CAST(0 AS BIT)
END & CASE
    WHEN [user].[update_date] >= '2018-03-23T00:00:01.0000000'
    THEN CAST(1 AS BIT) ELSE CAST(0 AS BIT)
END) = 1
go

问题出在 2018-03-24T00:00:01.0000000 格式中。 update_date 是 SQL 服务器 user table 中的 日期时间 列。

我该如何解决这个问题?非常感谢任何帮助。

更新:如果格式是2018-03-24T00:00:01.000,它是有效的。

首先,确保 EF Core 模型 "knows" SqlServer 数据库 table 列类型为 datetime(默认情况下 DateTime 属性映射到 datetime2 类型)通过使用 HasColumnType 流畅 API 或 [Column] 数据注释 - 请参阅 EF Core 文档中的 Data Types 主题。

如果您需要为很多列设置此项,您可以使用类似于的代码。

其次,(几乎)总是更好地参数化生成的 SQL 查询而不是使用常量值(文字)。您可以通过将值包装在常量 Tuple 和 return 引用 Item1 属性 的表达式中来实现。像这样:

static Expression ToExpression(object value)
{
    if (value == null) return Expression.Constant(null);
    var valueType = value.GetType();
    var closureType = typeof(Tuple<>).MakeGenericType(valueType);
    var closure = Activator.CreateInstance(closureType, value);
    return Expression.Property(Expression.Constant(closure), "Item1");
}

并在您的代码中使用它代替 Expression.Constant。执行此操作后,EF Core 将创建参数来代替当前文字。


旁注:如果您使用 Expression.AndAlso 而不是 Expression.And,您将获得更好的 SQL 翻译。前者表示逻辑与(C# && 运算符),而后者是按位与(C# & 运算符),您通常不会在逻辑表达式中使用它。 Expression.OrElseExpression.Or.

相同