#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. Молитвы оригинального постера были услышаны даже спустя долгое время после того, как я потерял надежду! Спасибо, я попытаюсь снова настроить этот проект и использовать ваше очень, очень желанное решение.