2010-04-26 57 views
3

在做LINQ到SQL在C#中,你可以做这样的事情:可组合FLinq表达式

var data = context.MyTable.Where(x => x.Parameter > 10); 

var q1 = data.Take(10); 
var q2 = data.Take(3); 

q1.ToArray(); 
q2.ToArray(); 

这将产生2个独立的SQL查询,一个与TOP 10,和其他与TOP 3。在玩Flinq时,我看到:

let data = query <@ seq { for i in context.MyTable do if x.Parameter > 10 then yield i } @> 

data |> Seq.take 10 |> Seq.toList 
data |> Seq.take 3 |> Seq.toList 

不是做同样的事情。在这里它似乎做了一个完整的查询,然后在客户端执行“take”调用。我看到的一个替代方案是:

let q1 = query <@ for i in context.MyTable do if x.Param > 10 then yield i } |> Seq.take 10 @> 
let q2 = query <@ for i in context.MyTable do if x.Param > 10 then yield i } |> Seq.take 3 @> 

这两个生成的SQL使用适当的TOP N过滤器。我的问题是它看起来不可组合。我基本上必须复制“where”子句,并且可能需要复制其他可能需要在基本查询上运行的其他子查询。有没有办法让F#给我更多的组合?

(我本来posted this question to hubfs,在那里我已经得到了一些答案,处理与C#进行查询转换“底”,即当需要数据,其中F#是急切地做这种转变的事实。)

回答

5

要在F#中执行此操作,您需要使用稍微不同的方法。而不是使用方法调用来组成查询(并且执行不成功),您需要构造带引号的F#代码。如果你只需要通过一些数字参数参数化的代码,你可以写在运行查询功能:

let takeData count = 
    <@ seq { for i in context.MyTable do 
      if x.Parameter > 10 then 
       yield i } 
    |> Seq.take count @> |> query 

这种方法只适用于简单的情况下,由于参数只能是数字。但是,您还可以通过允许您将其他操作添加到核心查询的方式撰写F#语句。

例如,假设您想将Seq.takeSeq.sortBy附加到查询的核心部分。这可以使用所谓的拼接运算符完成。内部报价(内<@ .. @>代码)可以用一个特殊的operato %,让您拼接另一个报价为您正在构建的一个:

let createQuery op = 
    <@ seq { for i in context.MyTable do 
      if x.Parameter > 10 then 
       yield i } 
    |> %op @> |> query 

这里,op参数是Expr<seq<MyTableRow> -> 'a>类型。您可以使用一些引用作为参数调用createQuery函数,并且它将在核心查询后追加参数。例如:

createQuery <@ Seq.take 10 @> 
createQuery <@ Seq.sortBy (fun x -> x.Parameter) @> 

这实际上比C#允许您执行的功能强大得多。我前段时间写了两篇文章,这一点:

  • 显示了一些F#的例子 - 不幸的是,它使用过时的语法,所以它不会直接工作,但它应该表现出的想法。在旧版本的F#中,您可以在报价中使用_,该报价自动创建了一个需要报价的函数(将拼接代替_作为参数)。这样,可以使用(你也不需要再好笑Unicode字符:-))被改写:

    (fun x -> <@ .. %x .. @>) 
    
  • Composing LINQ queries at runtime in C#展示了如何提供一些不直接提供C#的附加功能(使用一些技巧)