F#查询表达式生成

F#查询表达式生成,f#,f#-3.0,computation-expression,query-expressions,F#,F# 3.0,Computation Expression,Query Expressions,我正在学习f35;,现在正在阅读有关SQL类型提供程序使用的计算表达式和查询表达式的内容。我在做一些简单的任务,在某个时候需要连接(Union)2个查询,在阅读了序列和列表中的yield之后,我的第一个想法是在查询表达式中执行相同的操作,如下所示: query { yield! for a in db.As select { // projection } yield! for b in db.Bs select { // projection } } 这是无效的代码,然后我的

我正在学习
f35;
,现在正在阅读有关SQL类型提供程序使用的计算表达式和查询表达式的内容。我在做一些简单的任务,在某个时候需要连接(Union)2个查询,在阅读了序列和列表中的
yield
之后,我的第一个想法是在查询表达式中执行相同的操作,如下所示:

query {
    yield! for a in db.As select { // projection }
    yield! for b in db.Bs select { // projection }
}
这是无效的代码,然后我的第二种方法是使用以下代码“concat”它们:

seq {
    yield! query {...}
    yield! query {...}
}
或者像这样使用Linq的
Concat
函数:
(query{…}).Concat(query{…})
。如何做到这一点来自于这个问题的答案

不过,上述两种方法都有一个不同之处,使用
seq
将运行两个SQL查询,
Concat
只运行一个可以理解的查询

我的问题是:为什么查询表达式不支持
yield


编辑:


在进一步调查之后,我来到了,我看到了实现的
Yield
YieldFrom
方法,但没有
combined
Delay
方法,这对我来说现在更让人困惑

这是F#中查询表达式的一个很好的参考:

特别是,我认为该页面中的以下示例符合您的要求:

let query1 = query {
        for n in db.Student do
        select (n.Name, n.Age)
    }

let query2 = query {
        for n in db.LastStudent do
        select (n.Name, n.Age)
        }

query2.Union (query1)

屈服,并且可以在
select
通常为以下位置使用:

query { 
    for x in [5;2;0].AsQueryable() do 
    where (x > 1)
    sortBy x
    yield! [x; x-1] 
 } |> Seq.toList // [2;1;5;4]
但是,一般来说,您不能随意地散布查询和序列操作,因为很难定义它们应该如何组合:

query {
    for x in [1;2;3] do
    where (x > 1)
    while true do // error: can't use while (what would it mean?)
    sortBy x 
}
同样地:

query {
    for x in [1;2;3] do
    where (x > 1)
    sortBy x 
    yield! ['a';'b';'c']
    yield! ['x';'y';'z'] // error
}        
这有点模棱两可,因为不清楚第二个
是否会让步
位于
for
循环内,或在其后追加一组元素

因此,最好将查询视为查询,将序列视为序列,即使这两种计算表达式都支持某些相同的操作

通常情况下,查询自定义操作符是按元素工作的,因此表示联合或连接之类的内容很麻烦,因为它们处理的是整个集合,而不是单个元素。但如果您愿意,可以创建一个查询生成器,添加一个接受序列的
concat
自定义运算符,尽管它可能感觉有点不对称:

open System.Linq

type QB() =
    member inline x.Yield v = (Seq.singleton v).AsQueryable()
    member inline x.YieldFrom q = q
    [<CustomOperation("where", MaintainsVariableSpace=true)>]
    member x.Where(q:IQueryable<_>, [<ProjectionParameter>]c:Expressions.Expression<System.Func<_,_>>) = q.Where(c)
    [<CustomOperation("sortBy", MaintainsVariableSpace=true)>]
    member x.SortBy(q:IQueryable<_>, [<ProjectionParameter>]c:Expressions.Expression<System.Func<_,_>>) = q.OrderBy(c)
    [<CustomOperation("select")>]
    member x.Select(q:IQueryable<_>, [<ProjectionParameter>]c:Expressions.Expression<System.Func<_,_>>) = q.Select(c)
    [<CustomOperation("concat")>]
    member x.Concat(q:IQueryable<_>, q') = q.Concat(q')
    member x.For(q:IQueryable<'t>, c:'t->IQueryable<'u>) = q.SelectMany(fun t -> c t :> seq<_>) // TODO: implement something more reasonable here

let qb = QB()

qb {
    for x in ([5;2;0].AsQueryable()) do
    where (x > 1)
    sortBy x
    select x
    concat ([7;8;9].AsQueryable())
} |> Seq.toList
opensystem.Linq
类型QB()=
成员内联x.Yield v=(Seq.singleton v).AsQueryable()
成员内联x.YieldFrom q=q
[]
成员x.Where(q:IQueryable,[]c:Expressions.Expression)=q.Where(c)
[]
成员x.SortBy(q:IQueryable,[]c:Expressions.Expression)=q.OrderBy(c)
[]
成员x.Select(q:IQueryable,[]c:Expressions.Expression)=q.Select(c)
[]
成员x.Concat(q:IQueryable,q')=q.Concat(q'))
成员x.For(q:IQueryableIQueryableI我已经知道如何“连接/联合”这两个查询,这更像是一个哲学问题。既然已经可以这样做了,为什么不能使用其他计算表达式中使用的传统的
屈服!
,例如
Seq
List