【发布时间】:2012-11-29 12:06:33
【问题描述】:
有没有关于如何扩展/添加新关键字到查询表达式的文档或示例?这甚至可能吗?
例如,我想添加一个领先/滞后运算符。
【问题讨论】:
-
看看this question。我回答中的最后一个链接可能会引起您的兴趣。
标签: f#
有没有关于如何扩展/添加新关键字到查询表达式的文档或示例?这甚至可能吗?
例如,我想添加一个领先/滞后运算符。
【问题讨论】:
标签: f#
除了@pad 提到的query builder for the Rx Framework,还有来自 F# 团队的 Wonseok Chae 的演讲关于包含查询表达式的计算表达式。我不确定会议是否已录制,但有 very detailed slides 提供了一个关于生成 .NET IL 代码的查询语法的酷示例。
source code of the standard F# query builder 可能是了解支持哪些类型的操作以及如何使用属性对其进行注释的最佳资源。
where 子句展示了您可能需要的关键属性:
[<CustomOperation("where",MaintainsVariableSpace=true,AllowIntoPattern=true)>]
member Where :
: source:QuerySource<'T,'Q> *
[<ProjectionParameter>] predicate:('T -> bool) -> QuerySource<'T,'Q>
CustomOperation 属性定义操作的名称。 (非常重要的)参数MaintainsVariableSpace 允许您说该操作返回与输入相同类型的值。在这种情况下,之前定义的变量在操作后仍然可用。例如:
query { for p in db.Products do
let name = p.ProductName
where (p.UnitPrice.Value > 100.0M)
select name }
这里,变量p 和name 在where 之后仍然可以访问,因为where 只过滤输入,但不会转换列表中的值。
最后,ProjectionParameter 允许您说 p.UnitValue > 100.0M 实际上应该变成一个函数,该函数接受上下文(可用变量)并评估此表达式。如果你不指定这个属性,那么操作只是获取参数的值,如下所示:
query { for p in .. do
take 10 }
这里,参数10 只是一个简单的表达式,不能使用p 中的值。
【讨论】:
非常酷的语言功能。刚刚实现了反向查询 QuerySource。
简单的例子,但只是一个演示。
module QueryExtensions
type ExtendedQueryBuilder() =
inherit Linq.QueryBuilder()
/// Defines an operation 'reverse' that reverses the sequence
[<CustomOperation("reverse", MaintainsVariableSpace = true)>]
member __.Reverse (source : Linq.QuerySource<'T,System.Collections.IEnumerable>) =
let reversed = source.Source |> List.ofSeq |> List.rev
new Linq.QuerySource<'T,System.Collections.IEnumerable>(reversed)
let query = ExtendedQueryBuilder()
现在它被使用了。
let a = [1 .. 100]
let specialReverse =
query {
for i in a do
select i
reverse
}
【讨论】:
'Q 的两个地方都使用System.Collections.IEnumerable,因为标准的 IQueryable 提供程序不知道如何处理您的操作并且会引发运行时异常如果它被使用。
query { for x in myDatabaseTable do reverse } 之类的操作,其中myDatabaseTable 是一个LINQ 表,那么查询将在运行时失败。
'Q类型参数限制为IEnumerable,那么查询在运行时会成功,但是在客户端运行所有的逻辑(而不是试图生成一个服务器端查询)。