在c#中执行linq-to-sql时,你可以这样做:
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
没有做同样的事情。这里似乎做了一个完整的查询,然后在客户端进行“接听”调用。我看到的替代方案是:
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 @>
这两个用适当的TOP N过滤器生成SQL。我的问题是它似乎不可组合。我基本上必须复制“where”子句,并且可能必须复制我可能想要在基本查询上运行的其他其他子查询。有没有办法让F#给我一些更可组合的东西?
(我最初posted this question to hubfs,我得到了一些答案,处理了C#在“结束时”执行查询转换的事实,即当需要数据时,F#正在急切地进行转换。)
答案 0 :(得分: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.take
或Seq.sortBy
附加到查询的核心部分。这可以使用所谓的拼接运算符来完成。在引号内(<@ .. @>
内的代码),您可以使用特殊的操作符%
,它允许您拼接另一个引用到您正在构建的引号中:
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#允许你做的更强大。我前段时间写了两篇关于此的文章:
Composing LINQ queries at runtime in F#显示了一些F#示例 - 遗憾的是,它使用了过时的语法,所以它不会直接起作用,但它应该展示这些想法。在旧版本的F#中,您可以在引号中使用_
,它会自动创建一个带引号的函数(拼接代替_
作为参数)。这将不得不重写(你也不再需要有趣的Unicode字符: - )):
(fun x -> <@ .. %x .. @>)
Composing LINQ queries at runtime in C#展示了如何提供一些在C#中无法直接使用的其他功能(使用一些技巧)