Использование оператора сравнения меньше чем для строк в F # - PullRequest
1 голос
/ 13 июля 2020

Я пытаюсь написать запрос F # следующим образом:

let extract = 
    query {
        for trade in Schema.Trade do
        where (trade.Year + trade.Month <= "202007")
        take 100
        select (trade)
    }

Проблема в том, что и Год, и Месяц равны string.

Я вижу исключение из Microsoft.FSharp.Linq.QueryModule говоря, что

LessThanOrEqual не определено для типов 'System.String' и 'System.String'.

Почему бы и нет? Есть ли другой оператор?

Чтобы дать вам больше контекста, запрос выполняется внутри SQLProvider, и, если я напишу только = вместо <=, автоматически сгенерированный sql уже переводит + в правильный

CONCAT(`trade`.`year`, `trade`.`month`)

Так что эта часть не является проблемой на стороне SQLProvider.

C# базовый код

Позвольте мне перефразировать, что Я думаю происходит под прикрытием. Извините, это всего лишь мое увлечение анализом проблемы, не хочу путать вещи. В любом случае, я полагаю, что происходит что-то вроде следующего (это C# интерактивный)

> Expression<Func<String>> ex1 = () => "202006";;
> Expression<Func<String>> ex2 = () => "202005";;
> Expression.GreaterThan(ex1.Body, ex2.Body);;
System.InvalidOperationException: L'operatore binario GreaterThan non è definito per i tipi 'System.String' e 'System.String'.
  + System.Linq.Expressions.Expression.GetUserDefinedBinaryOperatorOrThrow(System.Linq.Expressions.ExpressionType, string, System.Linq.Expressions.Expression, System.Linq.Expressions.Expression, bool)
  + System.Linq.Expressions.Expression.GreaterThan(System.Linq.Expressions.Expression, System.Linq.Expressions.Expression, bool, System.Reflection.MethodInfo)

Stack Trace

Вместо этого это настоящая полная трассировка стека из вышеупомянутого F # программа.

</ExceptionType><Message>L'operatore binario LessThanOrEqual non è definito per i tipi 'System.String' e 'System.String'.</Message><StackTrace>   in System.Linq.Expressions.Expression.GetUserDefinedBinaryOperatorOrThrow(ExpressionType binaryType, String name, Expression left, Expression right, Boolean liftToNull)
   in System.Linq.Expressions.Expression.LessThanOrEqual(Expression left, Expression right, Boolean liftToNull, MethodInfo method)
   in Microsoft.FSharp.Linq.RuntimeHelpers.LeafExpressionConverter.ConvExprToLinqInContext@512-7.Invoke(Tuple`2 tupledArg)
   in Microsoft.FSharp.Linq.RuntimeHelpers.LeafExpressionConverter.ConvExprToLinqInContext(ConvEnv env, FSharpExpr inp) in F:\workspace\_work\1\s\src\fsharp\FSharp.Core\Linq.fs:riga 512
   in Microsoft.FSharp.Linq.RuntimeHelpers.LeafExpressionConverter.ConvExprToLinqInContext(ConvEnv env, FSharpExpr inp) in F:\workspace\_work\1\s\src\fsharp\FSharp.Core\Linq.fs:riga 734
   in Microsoft.FSharp.Primitives.Basics.List.mapToFreshConsTail[a,b](FSharpList`1 cons, FSharpFunc`2 f, FSharpList`1 x) in F:\workspace\_work\1\s\src\fsharp\FSharp.Core\local.fs:riga 241
   in Microsoft.FSharp.Primitives.Basics.List.map[T,TResult](FSharpFunc`2 mapping, FSharpList`1 x) in F:\workspace\_work\1\s\src\fsharp\FSharp.Core\local.fs:riga 252
   in Microsoft.FSharp.Linq.RuntimeHelpers.LeafExpressionConverter.ConvExprsToLinq(ConvEnv env, FSharpList`1 es) in F:\workspace\_work\1\s\src\fsharp\FSharp.Core\Linq.fs:riga 820
   in Microsoft.FSharp.Linq.RuntimeHelpers.LeafExpressionConverter.ConvExprToLinqInContext(ConvEnv env, FSharpExpr inp) in F:\workspace\_work\1\s\src\fsharp\FSharp.Core\Linq.fs:riga 642
   in Microsoft.FSharp.Primitives.Basics.List.map[T,TResult](FSharpFunc`2 mapping, FSharpList`1 x) in F:\workspace\_work\1\s\src\fsharp\FSharp.Core\local.fs:riga 250
   in Microsoft.FSharp.Linq.RuntimeHelpers.LeafExpressionConverter.ConvExprsToLinq(ConvEnv env, FSharpList`1 es) in F:\workspace\_work\1\s\src\fsharp\FSharp.Core\Linq.fs:riga 820
   in Microsoft.FSharp.Linq.RuntimeHelpers.LeafExpressionConverter.ConvExprToLinqInContext(ConvEnv env, FSharpExpr inp) in F:\workspace\_work\1\s\src\fsharp\FSharp.Core\Linq.fs:riga 642
   in Microsoft.FSharp.Linq.RuntimeHelpers.LeafExpressionConverter.ConvExprToLinqInContext(ConvEnv env, FSharpExpr inp) in F:\workspace\_work\1\s\src\fsharp\FSharp.Core\Linq.fs:riga 734
   in Microsoft.FSharp.Linq.RuntimeHelpers.LeafExpressionConverter.EvaluateQuotation(FSharpExpr e) in F:\workspace\_work\1\s\src\fsharp\FSharp.Core\Linq.fs:riga 844
   in Microsoft.FSharp.Linq.QueryModule.EvalNonNestedInner(CanEliminate canElim, FSharpExpr queryProducingSequence) in F:\workspace\_work\1\s\src\fsharp\FSharp.Core\Query.fs:riga 1823
   in Microsoft.FSharp.Linq.QueryModule.clo@1926-1.Microsoft-FSharp-Linq-ForwardDeclarations-IQueryMethods-Execute[a,b](FSharpExpr`1 q) in F:\workspace\_work\1\s\src\fsharp\FSharp.Core\Query.fs:riga 1928
   in Program.main(String[] argv) in C:\FunctionalCmd\Program.fs:riga 23</StackTrace><ExceptionString>System.InvalidOperationException: L'operatore binario LessThanOrEqual non è definito per i tipi 'System.String' e 'System.String'.
   in System.Linq.Expressions.Expression.GetUserDefinedBinaryOperatorOrThrow(ExpressionType binaryType, String name, Expression left, Expression right, Boolean liftToNull)
   in System.Linq.Expressions.Expression.LessThanOrEqual(Expression left, Expression right, Boolean liftToNull, MethodInfo method)
   in Microsoft.FSharp.Linq.RuntimeHelpers.LeafExpressionConverter.ConvExprToLinqInContext@512-7.Invoke(Tuple`2 tupledArg)
   in Microsoft.FSharp.Linq.RuntimeHelpers.LeafExpressionConverter.ConvExprToLinqInContext(ConvEnv env, FSharpExpr inp) in F:\workspace\_work\1\s\src\fsharp\FSharp.Core\Linq.fs:riga 512
   in Microsoft.FSharp.Linq.RuntimeHelpers.LeafExpressionConverter.ConvExprToLinqInContext(ConvEnv env, FSharpExpr inp) in F:\workspace\_work\1\s\src\fsharp\FSharp.Core\Linq.fs:riga 734
   in Microsoft.FSharp.Primitives.Basics.List.mapToFreshConsTail[a,b](FSharpList`1 cons, FSharpFunc`2 f, FSharpList`1 x) in F:\workspace\_work\1\s\src\fsharp\FSharp.Core\local.fs:riga 241
   in Microsoft.FSharp.Primitives.Basics.List.map[T,TResult](FSharpFunc`2 mapping, FSharpList`1 x) in F:\workspace\_work\1\s\src\fsharp\FSharp.Core\local.fs:riga 252
   in Microsoft.FSharp.Linq.RuntimeHelpers.LeafExpressionConverter.ConvExprsToLinq(ConvEnv env, FSharpList`1 es) in F:\workspace\_work\1\s\src\fsharp\FSharp.Core\Linq.fs:riga 820
   in Microsoft.FSharp.Linq.RuntimeHelpers.LeafExpressionConverter.ConvExprToLinqInContext(ConvEnv env, FSharpExpr inp) in F:\workspace\_work\1\s\src\fsharp\FSharp.Core\Linq.fs:riga 642
   in Microsoft.FSharp.Primitives.Basics.List.map[T,TResult](FSharpFunc`2 mapping, FSharpList`1 x) in F:\workspace\_work\1\s\src\fsharp\FSharp.Core\local.fs:riga 250
   in Microsoft.FSharp.Linq.RuntimeHelpers.LeafExpressionConverter.ConvExprsToLinq(ConvEnv env, FSharpList`1 es) in F:\workspace\_work\1\s\src\fsharp\FSharp.Core\Linq.fs:riga 820
   in Microsoft.FSharp.Linq.RuntimeHelpers.LeafExpressionConverter.ConvExprToLinqInContext(ConvEnv env, FSharpExpr inp) in F:\workspace\_work\1\s\src\fsharp\FSharp.Core\Linq.fs:riga 642
   in Microsoft.FSharp.Linq.RuntimeHelpers.LeafExpressionConverter.ConvExprToLinqInContext(ConvEnv env, FSharpExpr inp) in F:\workspace\_work\1\s\src\fsharp\FSharp.Core\Linq.fs:riga 734
   in Microsoft.FSharp.Linq.RuntimeHelpers.LeafExpressionConverter.EvaluateQuotation(FSharpExpr e) in F:\workspace\_work\1\s\src\fsharp\FSharp.Core\Linq.fs:riga 844
   in Microsoft.FSharp.Linq.QueryModule.EvalNonNestedInner(CanEliminate canElim, FSharpExpr queryProducingSequence) in F:\workspace\_work\1\s\src\fsharp\FSharp.Core\Query.fs:riga 1823
   in Microsoft.FSharp.Linq.QueryModule.clo@1926-1.Microsoft-FSharp-Linq-ForwardDeclarations-IQueryMethods-Execute[a,b](FSharpExpr`1 q) in F:\workspace\_work\1\s\src\fsharp\FSharp.Core\Query.fs:riga 1928
   in Program.main(String[] argv) in C:\FunctionalCmd\Program.fs:riga 23</ExceptionString></Exception></TraceRecord>
Eccezione non gestita di tipo 'System.InvalidOperationException' in FSharp.Core.dll
L'operatore binario LessThanOrEqual non è definito per i tipi 'System.String' e 'System.String'.

Ответы [ 3 ]

1 голос
/ 13 июля 2020

Не могли бы вы сделать что-нибудь подобное?

let extract = 
    let limitDate = System.DateTime(2020,08,01)
    query {
        for trade in Schema.Trade do
        where (trade < limitDate)
        take 100
        select (trade)
    }
1 голос
/ 13 июля 2020

У меня нет доступной базы данных, чтобы попробовать это, но я думаю, что проблема в том, что вы не можете разумно сравнивать строки в запросе SQL. Хотя логично объединять строки и проверять равенство строк, я не думаю, что сравнение строк даст то, что вы хотите.

Я бы попытался переписать условие, используя что-то вроде этого:

let extract = 
    query {
        for trade in Schema.Trade do
        where (int trade.Year < 2020 || 
          (int trade.Year = 2020 && int trade.Month <= 7))
        take 100
        select (trade)
    }

Я предполагаю, что вам нужен запрос, который выбирает сделки до июля 2020 года, поэтому я закодировал это, используя более сложное условие, которое принимает либо сделки до 2020 года, либо сделки 2020 года до или в июле.

0 голосов
/ 14 июля 2020

Это очень похоже на C# эквивалент, описанный здесь .

Действительно, если мы посмотрим на linq2db ExpressionEqualityComparer , они указывают на CompareBinary

в то время как SQLProvider SqlRuntime.Patterns указывает на ConditionOperator.GreaterThan

| ExpressionType.GreaterThan,        (:? BinaryExpression as ce) -> Some (ConditionOperator.GreaterThan,  ce.Left,ce.Right)

Это может быть (см. Комментарий @Abel)

a ошибка в переводе запроса, так как string реализует IComparable

Я решил эту проблему, переключившись на C# (это более надежно и предотвращает исключение времени выполнения с ошибкой компиляции, согласованное с его синтаксисом, противоположным тому, что происходит в F #) и linq2db (который также является более зрелым и надежным, чем поставщик типов F # SQL)

...