在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();
Run Code Online (Sandbox Code Playgroud)
这将产生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
Run Code Online (Sandbox Code Playgroud)
是不是做同样的事情.这里似乎做了一个完整的查询,然后在客户端进行"接听"调用.我看到的替代方案是:
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 @>
Run Code Online (Sandbox Code Playgroud)
这两个用适当的TOP N过滤器生成SQL.我的问题是它似乎不可组合.我基本上必须复制"where"子句,并且可能必须复制我可能想要在基本查询上运行的其他其他子查询.有没有办法让F#给我一些更可组合的东西?
(我最初将这个问题发布到hubfs,在那里我得到了一些答案,处理了C#在"结束时"执行查询转换的事实,即当需要数据时,F#正急切地进行转换.)
要在F#中执行此操作,您需要使用稍微不同的方法.您需要构造引用的F#代码,而不是使用方法调用(和延迟执行)来组成查询.如果您只需要通过某个数字参数来参数化代码,则可以编写一个运行查询的函数:
let takeData count =
<@ seq { for i in context.MyTable do
if x.Parameter > 10 then
yield i }
|> Seq.take count @> |> query
Run Code Online (Sandbox Code Playgroud)
这仅适用于简单情况,因为参数只能是一个数字.但是,您也可以以允许您向核心查询添加其他操作的方式编写F#引用.
例如,让我们说,你想要么追加Seq.take或Seq.sortBy对查询的核心部分.这可以使用所谓的拼接操作符来完成.在内部引用(代码内<@ .. @>)中,您可以使用特殊的operato %,它允许您将另一个引用拼接到您正在构建的引用中:
let createQuery op =
<@ seq { for i in context.MyTable do
if x.Parameter > 10 then
yield i }
|> %op @> |> query
Run Code Online (Sandbox Code Playgroud)
这里,op参数是类型Expr<seq<MyTableRow> -> 'a>.您可以createQuery使用一些引用作为参数调用该函数,它将在核心查询之后附加参数.例如:
createQuery <@ Seq.take 10 @>
createQuery <@ Seq.sortBy (fun x -> x.Parameter) @>
Run Code Online (Sandbox Code Playgroud)
这实际上比C#允许你做的更强大.我前段时间写了两篇关于此的文章:
在F#中在运行时编写LINQ查询显示了一些F#示例 - 遗憾的是,它使用了过时的语法,因此它不会直接起作用,但它应该展示这些想法.在旧版本的F#中,您可以_在引号中使用它自动创建一个带引号的函数(拼接代替_作为参数).这将不得不重写(你也不再需要有趣的Unicode字符:-)):
(fun x -> <@ .. %x .. @>)
Run Code Online (Sandbox Code Playgroud)在C#中运行时编写LINQ查询显示了如何提供一些在C#中不能直接使用的附加功能(使用一些技巧)