【问题标题】:Extending Query Expressions扩展查询表达式
【发布时间】:2012-11-29 12:06:33
【问题描述】:

有没有关于如何扩展/添加新关键字到查询表达式的文档或示例?这甚至可能吗?

例如,我想添加一个领先/滞后运算符。

【问题讨论】:

  • 看看this question。我回答中的最后一个链接可能会引起您的兴趣。

标签: f#


【解决方案1】:

除了@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 }

这里,变量pnamewhere 之后仍然可以访问,因为where 只过滤输入,但不会转换列表中的值。

最后,ProjectionParameter 允许您说 p.UnitValue &gt; 100.0M 实际上应该变成一个函数,该函数接受上下文(可用变量)并评估此表达式。如果你不指定这个属性,那么操作只是获取参数的值,如下所示:

query { for p in .. do
        take 10 }

这里,参数10 只是一个简单的表达式,不能使用p 中的值。

【讨论】:

  • 值得一提的是,这些可以是标准查询构建器类型的扩展方法。
  • 非常酷!感谢您的链接。有趣的是,人们也可以使用现有的 linq 扩展来完成他们想要的。
  • @kvb 太酷了!但是您还需要补充一点,它们不能与 LINQ to SQL 和 LINQ to Entities 的标准提供程序一起使用 :-( (除非您编写一些预处理器将它们转换为表达式树中支持的其他东西? )
  • 对,它们只适用于 IEnumerables,而不适用于 IQueryables。这可以通过确保输入和输出类型更具体来强制执行(请参阅我对 davewolfs 的其他答案的评论)。
【解决方案2】:

非常酷的语言功能。刚刚实现了反向查询 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 提供程序不知道如何处理您的操作并且会引发运行时异常如果它被使用。
  • 它似乎运行良好,但会添加。你能更详细地解释一下吗?他们知道如何处理?
  • 基本上,IQueryable 提供程序针对查询的引用版本工作,并且仅识别内置运算符。因此,如果您执行query { for x in myDatabaseTable do reverse } 之类的操作,其中myDatabaseTable 是一个LINQ 表,那么查询将在运行时失败。
  • 如果你将'Q类型参数限制为IEnumerable,那么查询在运行时会成功,但是在客户端运行所有的逻辑(而不是试图生成一个服务器端查询)。
猜你喜欢
  • 2015-02-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-22
  • 1970-01-01
  • 1970-01-01
  • 2021-10-24
  • 1970-01-01
相关资源
最近更新 更多