как исправить ошибку SqlError: сбой при преобразовании даты и / или времени из символьной строки. EF двоичное выражение?

#c# #sql #linq #sqlclient

#c# #sql #linq ( ссылка ) #sqlclient #linq

Вопрос:

Я пытаюсь использовать выражения для динамического создания операций сравнения для использования с Entity Framework Core 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, и оно выполняется.

Любая помощь или подсказки для невежественных приветствуются. Слишком много поиска в Google и ничего не получается. Каждый пример представляет собой строки 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 amp;amp; 
    type.GetGenericTypeDefinition().Equals(typeof(Nullable<>));
}
  

Я ожидаю возврата красивого результирующего набора вместо раздражающей ошибки.

Комментарии:

1. 1. Используйте профилировщик запросов SQL, чтобы увидеть, что отправляется по проводам. 2. Используйте перечисление вместо значений int для вашей comparer переменной.

2. В c # вы должны использовать DateTime. Выполните синтаксический анализ (string), чтобы преобразовать строку date в объект DateTime и передать DateTime в качестве параметра в запрос. SQL server автоматически выполнит синтаксический анализ, а c # — нет.

3. Двоичное выражение принимает даты в качестве параметров. Что-то в отправке вызывает ошибку. Я воспользуюсь этим, всегда делаю, и сообщу ответ. Конечно, боги кода не ограничивают эту пытку только мной.

4. Вы когда-нибудь использовали это? Я сталкиваюсь с той же проблемой.

5. Нет, я не использовал это, но я уволен, поэтому попробую еще раз. Если у меня будет победа, обязательно опубликую исправление.

Ответ №1:

Сопоставления типов данных SQL Server — это решение проблемы. Согласно моим исследованиям, объект SqlCommand использует сопоставление типов данных для преобразования из CLR в типы данных 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-03T14:32:05», то сервер sql был бы доволен, но, увы, выражения настолько строги в отношении типа данных, и вы не можете передать этот формат строки для свойства DateTime.

Хватит болтать, давайте пройдемся по некоторому коду:

 ...

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

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

...
  

Лямбда-выражение:

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

выполняет трюк и позволяет DbSet генерировать команду с параметрами sql.

После выполнения с помощью DbSet результирующий IQueryable будет представлять собой команду с параметрами. Это можно проверить из сгенерированного sql, и сопоставление типов данных теперь может преобразовать тип данных CLR в тип данных SQL, не выдавая страшное «Ошибка преобразования при преобразовании даты и / или времени из символьной строки».

Комментарии:

1. Молитвы оригинального постера были услышаны даже спустя долгое время после того, как я потерял надежду! Спасибо, я попытаюсь снова настроить этот проект и использовать ваше очень, очень желанное решение.