【问题标题】:Expression<Func<T, bool>> from a F# func来自 F# func 的 Expression<Func<T, bool>>
【发布时间】:2012-02-03 19:36:32
【问题描述】:

在 linq 中,.Where 需要一个 Expression> 谓词,我可以在 F# 中将其写为

<@ fun item:'a -> condition @>    // Expr<'a -> bool>

我正在使用 FSharp.Powerpack 从引用中构建表达式,但它给我的是 MethodCallExpression。深入研究,powerpack 代码正确构建了 lambda,但将其包装在 Convert 调用中(为什么会这样?)。我想知道将参数转换为方法调用(lambda)是否最终会给我我需要的 Expression>。

所以问题是为什么调用 Convert ,以及如何实际获取具有 Func 签名的 lambda。

【问题讨论】:

标签: linq f#


【解决方案1】:

我不记得我是在哪里找到这段代码的,但这是我用来将Expr&lt;'a -&gt; 'b&gt; 转换为Expression&lt;Func&lt;'a, 'b&gt;&gt; 的方法。希望这能解决您的问题。

open System
open System.Linq.Expressions
open Microsoft.FSharp.Quotations
open Microsoft.FSharp.Linq.QuotationEvaluation

let toLinq (expr : Expr<'a -> 'b>) =
  let linq = expr.ToLinqExpression()
  let call = linq :?> MethodCallExpression
  let lambda = call.Arguments.[0] :?> LambdaExpression
  Expression.Lambda<Func<'a, 'b>>(lambda.Body, lambda.Parameters) 

【讨论】:

  • expr.ToLinqExpression() 现在在 F# 中为 Microsoft.FSharp.Linq.RuntimeHelpers.LeafExpressionConverter.QuotationToExpression expr
【解决方案2】:

您现在可以做到这一点的一种方法是利用 F# 将在调用 .NET 类型(期望 Expression&lt;Func&lt;...&gt;&gt;)上的方法时自动执行此转换的事实。

我不完全确定何时将它添加到语言中,但肯定使用 F# 4,您不需要将 F# 表达式显式转换为 LINQ 表达式。如果您最初想要这样做的原因是能够使用 IQueryable LINQ API(或其他基于表达式的 .NET API),那么它现在可以毫不费力地工作,例如:

someEfDataContext.MyEntities.Single(fun e -> e.Id = 42)

只是工作。尽管这看起来像一个普通的 lambda(我们没有使用 F# 的表达式语法),但它会编译为生成 F# 表达式对象的代码,然后将其传递给 LeafExpressionConverter‌​.QuotationToExpressi‌on 以将其转换为 LINQ 表达式对象。

但有时您会希望直接在 F# 中获取 LINQ 样式的表达式对象。 (例如,有时编写一个 F# 函数来生成将在多个查询中使用的表达式会很有用。)在这种情况下,您可以编写这样的帮助程序:

type FunAs() =
    static member LinqExpression<'T, 'TResult>(e: Expression<Func<'T, 'TResult>>) = e

这看起来什么都不做——它只是返回它的参数。但是,由于FunAs 是一种.NET 类型,F# 将自动将使用fun 表达式调用它的任何调用站点编译为生成合适的LINQ 查询表达式的代码。例如:

let linqExpr = FunAs.LinqExpression(fun (e:MyEntity) -> e.Id = 42)

这里,linqExpr 将是 Expression&lt;Func&lt;MyEntity, bool&gt;&gt; 类型。

关键在于此方法是 .NET 类型的成员。如果你用普通的 F# 函数尝试完全相同的事情:

let funAsLinqExpression<'T, 'TResult>(e: Expression<Func<'T, 'TResult>>) = e

这似乎应该和FunAs.LinqExpression的意思完全一样,你会发现你不能以同样的方式调用它。例如,如果你试试这个:

let linqExpr = funAsLinqExpression(fun (e:MyEntity) -> e.Id = 42)

你会得到一个(有点无用的)错误:“这个函数接受了太多参数,或者在不期望函数的上下文中使用了”。

通过使该函数成为 .NET 类型的成员,我们可以利用 F# 的有用“您似乎正在调用需要 LINQ 样式表达式的 .NET API,让我为您处理”功能.

(可能有一些更明确的方法可以让 LINQ 编译器为您执行相同的技巧,而无需将 .NET 类型带入图片中,但我没有找到它。)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-17
    • 2023-03-31
    相关资源
    最近更新 更多