linq 如何修复SqlError:从字符串转换日期和/或时间时失败,EF binaryexpression?

oymdgrw7  于 12个月前  发布在  其他
关注(0)|答案(2)|浏览(93)

我试图使用表达式来动态创建比较操作,以与实体框架核心2.2一起使用。
这是查询

new System.Linq.Expressions.Expression.MethodCallExpressionProxy(((Remotion.Linq.QueryableBase<ORMModel.Sale>)tq).Expression).DebugView

.Call System.Linq.Queryable.Where(
.Constant<Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable`1[ORMModel.Sale]>(Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable`1[ORMModel.Sale]),
'(.Lambda #Lambda1<System.Func`2[ORMModel.Sale,System.Boolean]>))

.Lambda #Lambda1<System.Func`2[ORMModel.Sale,System.Boolean]>(ORMModel.Sale $Sale) {
$Sale.CreateDT > (System.DateTime).Constant<System.DateTime>(12/25/2018 5:00:00 AM)
}

字符串
这是我得到的错误
System.Data.SqlClient.SqlError
从字符串转换日期和/或时间时转换失败。
我已经在SQL Server Management Studio中直接使用了该日期值,并且它会执行。
任何帮助或线索的无能赞赏。太多的谷歌搜索和无处可去。每个例子都是SQL字符串,而不是动态表达式。

public static Expression GetBinaryExpression<TSource>(string propertyName, int comparer, dynamic valueIn)
{
    PropertyInfo pi = GetPropertyInfo<TSource>(propertyName);
    Type pt = pi.PropertyType;

    var parameter = Expression.Parameter(typeof(TSource), typeof(TSource).Name);
    var property = Expression.Property(parameter, propertyName);

    dynamic valueTyped;

    if (IsNullableType(pt))
    {
        valueTyped = Convert.ChangeType(valueIn, Nullable.GetUnderlyingType(pt));
    }
    else
    {
        valueTyped = Convert.ChangeType(valueIn, pt);
    }

    var constant = Expression.Constant(valueTyped);

    var val = Expression.Convert(constant, pt);

    Expression exp = null;

    switch (comparer)
    {
        case 1:
            exp = Expression.Equal(property, val);
            break;
        case 2:
            exp = Expression.NotEqual(property, val);
            break;
        case 3:
            exp = Expression.GreaterThan(property, val);
            break;
        case 4:
            exp = Expression.GreaterThanOrEqual(property, val);
            break;
        case 5:
            exp = Expression.LessThan(property, val);
            break;
        case 6:
            exp = Expression.LessThanOrEqual(property, val);
            break;
    }

    if (exp.CanReduce) exp = exp.Reduce();

    return Expression.Lambda<Func<TSource, Boolean>>(exp, parameter);
}

private static bool IsNullableType(Type type)
{
    return type.IsGenericType && 
    type.GetGenericTypeDefinition().Equals(typeof(Nullable<>));
}


我希望返回一个漂亮的结果集,而不是一个令人恼火的错误。

r7s23pms

r7s23pms1#

SQL Server数据类型Map是解决这个问题的方法,根据我的研究,SqlCommand对象依赖于数据类型Map来将SQL数据类型转换为SQL数据类型。
当涉及到DateTime时,使用常量表达式没有帮助,因为它被序列化为这样的字符串格式:
'2020-05-03T14:32:05.000435Z'
例如,生成的SQL看起来像这样:

SELECT [s].[Routine], [s].[CreatedOn] FROM Routines as s
WHERE [s].[CreatedOn] >= '2020-05-03T14:32:05.000435Z'

字符串
如果日期字符串格式是这样的:'' 2020 -05- 03 T14:32:05',那么SQL服务器会很高兴,但是表达式对数据类型非常严格,你不能为DateTime属性传递这个字符串格式。
废话少说,让我们来看看代码:

...

Expression<Func<TProperty>> exprValueVar = () => valueTyped;

exp = Expression.Equal(property, exprDateVar.Body);

...


lambda表达式:

Expression<Func<TProperty>> exprValueVar = () => valueTyped;


这样做的技巧,并允许DbSet生成一个sql参数化命令。
一旦使用DbSet执行,生成的IQueryable将是一个带参数的命令。这可以从生成的SQL和数据类型Map中进行验证,现在可以将SQL数据类型转换为SQL数据类型,而不会抛出可怕的“转换日期和/或时间时转换失败字符串”。

yzuktlbb

yzuktlbb2#

Wizzyno的答案为我解决了这个问题。下面是完整的实现:

private static Expression GetValueExpression(object valueTyped)
{
    if (valueTyped is DateTime)
    {
        var dateTimeValue = (DateTime)valueTyped;
        Expression<Func<DateTime>> exprValueVar = () => dateTimeValue;
        return exprValueVar.Body;
    }

    return Expression.Constant(valueTyped);
}

字符串
使用...

var valueExpression = GetValueExpression(typedValue);
exp = Expression.Equal(property, valueExpression);

相关问题