SqlProvider递归组合查询

3
使用SqlProvider类型提供程序,我正在尝试递归地折叠“查询条件”列表。
type Criterion = {
    Column : string
    Operator : string
    Value : string
}

这样,表达式树只会被编译成SQL一次,而且我不会多次访问数据库。我尝试了几种方法,其中最成功的方法类似于以下内容:

let rec eval (acc : IQueryable<SourceEntity> option) (qrys : Criterion list) =
match qrys with
|[] -> acc
|x :: xs -> let acc' = let op,valu = translateOpnValu x
                       match acc with
                       |Some acc' -> query {
                                            for elem in acc' do
                                            where (elem.GetColumn x.Column op valu)
                                            select elem 
                                     } |> Some
                       |None     -> query {
                                            for elem in ctx.Dbo.Source do
                                            where (elem.GetColumn x.Column op valu)
                                            select elem 
                                     } |> Some
            eval acc' xs

函数 translateOpnValu 的位置在哪里

let translateOpnValu (c:Criterion) =
     match c.Operator with
     |"%=%" -> (=%), sprintf "%%%s%%" c.Value
     |_     -> (=), c.Value

我正在遇到这个异常。
System.Exception: Unsupported expression. Ensure all server-side objects appear on the left hand side of predicates.  The In and Not In operators only support the inline array syntax. InvokeFast(elem.GetColumn("Source Code"), value(FSI_0006+acc'@38-2), "%BEN%")
at Microsoft.FSharp.Linq.RuntimeHelpers.LeafExpressionConverter.EvaluateQuotation(FSharpExpr e)
at Microsoft.FSharp.Linq.QueryModule.EvalNonNestedInner(CanEliminate canElim, FSharpExpr queryProducingSequence)
at Microsoft.FSharp.Linq.QueryModule.EvalNonNestedOuter(CanEliminate canElim, FSharpExpr tm)
at Microsoft.FSharp.Linq.QueryModule.clo@1735-1.Microsoft-FSharp-Linq-ForwardDeclarations-IQueryMethods-Execute[a,b](FSharpExpr`1 )
at FSI_0006.evaluate(FSharpOption`1 acc, FSharpList`1 qrys) in F:\code_root\vs2015\F\CAMS\CAMS\scratch.fsx:line 47
at <StartupCode$FSI_0007>.$FSI_0007.main@() in F:\code_root\vs2015\F\CAMS\CAMS\scratch.fsx:line 60

如果我用隐式运算符(= / =%)替换从translateOpnValu返回的'op',那么它可以正常工作。
我有一种感觉是因为返回的运算符类型被限制为(字符串 -> 字符串 -> 布尔值),而隐式运算符更加通用。如何让translateOpnValu函数返回更通用的运算符呢?或者也许问题根本不在这里...

1
由于您的 op 没有命名为运算符,因此无法以中缀表示法应用它。请尝试使用前缀:op (elem.GetColumn x.Column) valu - Fyodor Soikin
谢谢,好建议,但不幸的是,我仍然得到相同的异常... - Ben Lynch
1
那么我会认为SqlProvider的引号解析器只期望操作符本身,而不是以任何方式别名。尝试从translateOpnValu返回引号并将其拼接。 - Fyodor Soikin
1个回答

3

@Fyodor是正确的 - 为了让SQL提供程序正确地捕捉到您的函数,您需要将它用引号括起来并将其插入查询表达式中。像这样的语句应该可以工作:

let translateOpnValu (c:Criterion) =
     match c.Operator with
     |"%=%" -> <@ (=%) @>, sprintf "%%%s%%" c.Value
     |_     -> <@ (=) @>, c.Value

// ...

query {
    for elem in acc' do
    where ((%op) (elem.GetColumn x.Column) valu)
    select elem 
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接