您现在可以做到这一点的一种方法是利用 F# 将在调用 .NET 类型(期望 Expression<Func<...>>)上的方法时自动执行此转换的事实。
我不完全确定何时将它添加到语言中,但肯定使用 F# 4,您不需要将 F# 表达式显式转换为 LINQ 表达式。如果您最初想要这样做的原因是能够使用 IQueryable LINQ API(或其他基于表达式的 .NET API),那么它现在可以毫不费力地工作,例如:
someEfDataContext.MyEntities.Single(fun e -> e.Id = 42)
只是工作。尽管这看起来像一个普通的 lambda(我们没有使用 F# 的表达式语法),但它会编译为生成 F# 表达式对象的代码,然后将其传递给 LeafExpressionConverter.QuotationToExpression 以将其转换为 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<Func<MyEntity, bool>> 类型。
关键在于此方法是 .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 类型带入图片中,但我没有找到它。)